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 libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: ActionUtil.c /main/14 1999/02/19 13:10:09 mgreess $ */
24 /*************************************<+>*************************************
25 *****************************************************************************
31 ** Description: This file contains the action library utility source code.
33 ** (c) Copyright 1993, 1994 Hewlett-Packard Company
34 ** (c) Copyright 1993, 1994 International Business Machines Corp.
35 ** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
36 ** (c) Copyright 1993, 1994 Novell, Inc.
37 ****************************************************************************
38 ************************************<+>*************************************/
45 #include <sys/types.h>
47 #include <sys/param.h> /* for *MAX* macros */
48 #define X_INCLUDE_NETDB_H
49 #define X_INCLUDE_GRP_H
50 #define XOS_USE_XT_LOCKING
51 #include <X11/Xos_r.h>
53 #ifdef _SUN_OS /* Need this for the strtod () call */
54 #include <floatingpoint.h>
62 #include <X11/Xlib.h> /* for DisplayString */
63 #include <X11/Xresource.h> /* for X resource defs */
64 #include <X11/Intrinsic.h> /* for X toolkit functions */
66 #include <Dt/ActionP.h>
67 #include <Dt/ActionUtilP.h>
68 #include <Dt/CmdInvP.h>
69 #include <Dt/DtNlUtils.h>
70 #include <Dt/Indicator.h>
72 #include "myassertP.h"
73 #include "DtSvcLock.h"
75 /******************************************************************************
77 * List of active DtActionInvoke() DtActionInvocationID's
79 *****************************************************************************/
81 static _DtActInvRecT **_DtActInvRecArray; /* top of array */
82 static int _actInvRecArraySize; /* size of array */
84 static void _DtActFreeInvRec(); /* free an inv rec */
85 static void _DtActFreeChildRec( _DtActChildRecT *childp);
88 /* I want to exercise the realloc code for now */
89 #define _START_INVREC_SIZE 1 /* initial array size */
91 #define _START_INVREC_SIZE 32 /* initial array size */
95 #define P_tmpdir "/var/tmp"
98 extern void _DtFreeRequest(ActionRequest *request );
101 /*******************************************************************************
102 * _DtBasename -- utiltiy function to return a pointer to a
103 * string containing the basename of a file name. It doesn't
104 * modifiy the original string parameter. If the original file
105 * path ends in "/" the last component of the path is returned as
107 ******************************************************************************/
109 _DtBasename( const char *s )
112 char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1];
119 /* Work on a local copy of the original string */
123 #ifdef _Dt_HOST_COLON_PATH_SUPPORT
124 /* Chop off the "host:" if necessary */
125 if ( basep = DtStrchr(p,'/') )
127 /* if ( basep > p && (*(basep - 1) == ':') ) */
128 if ( basep > p && ( *DtPrevChar(p,basep) == ':') )
131 #endif /* _Dt_HOST_COLON_PATH_SUPPORT */
133 if ( (basep = DtStrrchr(p,'/')) == NULL )
134 return XtNewString(p);
137 if (( basep == p) && (strlen(basep) == 1))
138 return XtNewString("/");
141 * check for trailing slash
143 /* while (basep == p+strlen(p)-1 ) */
144 while (basep == DtPrevChar(p,p+strlen(p)) )
146 myassert( *basep == '/');
148 return XtNewString(basep);
149 *basep = '\0'; /* replace trailing slash */
150 if ( (basep = DtStrrchr(p,'/')) == NULL )
151 return XtNewString(p);
154 /* skip past '/' before returning basename */
156 return XtNewString(basep);
161 /*******************************************************************************
162 * _DtPathString -- returns the path portion of the "host:/path" string
163 * passed in as a parameter.
164 * The incoming string is assumed to be in : "[host:]/path format.
165 * NOTE: a FULL path name is required.
166 ******************************************************************************/
168 _DtPathname( const char *s)
178 /* Chop off the "host:" if necessary */
179 slashp = DtStrchr((char *)s,'/');
181 /* if ( slashp > s && (*(slashp - 1) != ':') ) */
182 if ( slashp > s && (*DtPrevChar(s,slashp) != ':') )
185 * full path name required
186 * --- should never get here
192 return XtNewString(slashp);
195 /*******************************************************************************
196 * _DtDirname -- returns the directory portion of the file
197 * path string passed in as a parameter. The original string
198 * may be modified to remove trailing slashes.
199 * The incoming string is assumed to be in : "[host:/]dir/file" format.
200 ******************************************************************************/
202 _DtDirname( const char *s)
206 char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1];
210 /* Work on a local copy of the original string */
214 #ifdef _Dt_HOST_COLON_PATH_SUPPORT
215 /* Chop off the "host:" if necessary */
216 if ( slashp = DtStrchr(p,'/') )
218 /* if ( slashp > p && (*(slashp - 1) == ':') ) */
219 if ( slashp > p && (*DtPrevChar(p,slashp) == ':') )
222 #endif /* _Dt_HOST_COLON_PATH_SUPPORT */
224 /* handle multiple trailing slashes */
226 while ( (slashp = DtStrrchr(p,'/')) )
230 * Special case '/' -- return '/'
233 return XtNewString("/");
236 * Is this a trailing slash ?
237 * -- then try again else break
239 if ( slashp == DtPrevChar(p,p + strlen(p)) )
249 * Replace the last '/' with a NULL to get the
252 dirp = XtNewString(p);
253 *(dirp + (slashp - p)) = '\0';
257 /*****************************************************************************
260 * Extract the host name string from files in the [host:][/][path/]file
261 * format. This function mallocs space for a new copy of the host
262 * name string; it is up to the caller to free this space.
263 * This function requires that names which include the hoststring
264 * use a "full path" name to specify the file location. If the hoststring
265 * is omitted the path may be relative.
268 * If a host name string can be found; a pointer to a newly
269 * malloced copy of the host name string is returned; otherwise
270 * a NULL pointer is returned.
272 * NOTE: This function should NOT be used to extract host name strings
273 * from display variables.
275 *****************************************************************************/
278 _DtHostString( const char *s)
282 char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1];
285 if ( (slashp = DtStrchr((char *)s,'/')) == NULL )
289 * Make a local copy of the string to avoid problems modifying
295 /* if ( (slashp > s) && (*(slashp -1) == ':' )) */
296 if ( (slashp > s) && (*DtPrevChar(s,slashp) == ':' ))
298 /* *(p + (slashp - s - 1)) = NULL; */
299 *(p + (DtPrevChar(s,slashp) - s)) = '\0';
300 host = XtNewString(p);
309 _DtGetSessionHostName( void )
311 static char *sessionHostName = NULL;
314 if ( sessionHostName && *sessionHostName ) {
315 _DtSvcProcessUnlock();
316 return XtNewString(sessionHostName);
319 sessionHostName = getenv(ENV_SESSION_SVR);
321 if ( sessionHostName && *sessionHostName ) {
322 _DtSvcProcessUnlock();
323 return XtNewString(sessionHostName);
327 * Default to display host name if the session host environment
328 * variable is not set or set to null. The command invoker
329 * initialization should have tucked away the display name.
332 sessionHostName = _DtGetDisplayHostName( (Display *) NULL );
333 _DtSvcProcessUnlock();
334 return XtNewString(sessionHostName);
339 _DtGetDisplayHostName( Display *dp)
341 static char *displayHostName = NULL;
342 char *tmpName = NULL;
347 if ( displayHostName && *displayHostName ) {
348 _DtSvcProcessUnlock();
349 return XtNewString(displayHostName);
352 tmpName = XtMalloc(MAXHOSTNAMELEN + 5 + 1);
358 * If a display pointer has been provided use it to determine
362 strcpy(tmpName,DisplayString(dp));
363 if (( tmp = DtStrrchr(tmpName,':') ))
366 displayHostName = XtNewString(tmpName);
373 * In the absence of a display pointer, use the
374 * DISPLAY environment variable.
376 memset(tmpName, 0, (MAXHOSTNAMELEN + 5) + 1);
377 strncpy(tmpName, getenv("DISPLAY"), (MAXHOSTNAMELEN + 5));
378 if (( tmp = DtStrrchr(tmpName,':') ))
381 displayHostName = XtNewString(tmpName);
387 * Check for degenerate forms of the display name
389 if ( !( displayHostName &&
391 strcmp(displayHostName,"local") &&
392 strcmp(displayHostName, "unix") ))
395 * default to localHostName
398 XtFree(displayHostName);
399 displayHostName = _DtGetLocalHostName();
402 myassert( displayHostName && *displayHostName );
403 myassert( (DtStrchr( displayHostName, ':' ) == NULL) );
405 _DtSvcProcessUnlock();
406 return XtNewString(displayHostName);
409 /******************************************************************************
411 * _DtGetLocalHostname:
412 * return the short form of the local host name.
413 * (i.e. truncate the hostname at the first '.' character).
414 ******************************************************************************/
417 _DtGetLocalHostName( void )
419 static char *localHostName = NULL;
420 static char hostNameBuf[MAXHOSTNAMELEN + 1];
424 if ( localHostName && *localHostName ) {
425 _DtSvcProcessUnlock();
426 return XtNewString(localHostName);
429 if ( gethostname(hostNameBuf, sizeof(hostNameBuf)) ) {
430 _DtSvcProcessUnlock();
431 return NULL; /* failed gethostname */
433 if ((ptr = DtStrchr(hostNameBuf, '.')))
434 *ptr = '\0'; /* delete domain name if there is one */
436 localHostName = hostNameBuf;
437 _DtSvcProcessUnlock();
438 return XtNewString(localHostName);
441 /******************************************************************************
444 * return True if the two host names provided are actually references to
445 * the same host; False otherwise. If either host1 or host2 is a NULL
446 * pointer use the "local host" name.
448 ******************************************************************************/
452 _DtIsSameHost(const char *host1, const char *host2)
454 char hostName1[MAXHOSTNAMELEN + 1];
455 char hostName2[MAXHOSTNAMELEN + 1];
456 struct hostent *host_ret;
457 _Xgethostbynameparams host_buf;
461 * If either parameter is null; use the local host name in its stead
465 tp = _DtGetLocalHostName();
466 strcpy(hostName1,tp);
471 strcpy(hostName1,host1);
476 tp = _DtGetLocalHostName();
477 strcpy(hostName2,tp);
482 strcpy(hostName2,host2);
486 * We now have local copies of the hostnames in the
487 * arrays hostName1 and hostName2. Truncate the names
488 * to their short form before doing the compare.
490 if ( (tp = DtStrchr(hostName1,'.')) != NULL )
492 if ( (tp = DtStrchr(hostName2,'.')) != NULL )
496 * Try to avoid querying the name server (or /etc/hosts).
497 * Do the name strings match?
500 if ( !strcmp(hostName1,hostName2) )
503 if ( (host_ret = _XGethostbyname(hostName1, host_buf)) == NULL )
504 return False; /* treat them as different on failure */
507 * Save the data from gethostbyname() in "hostName1" so we can
508 * call gethostbyname() again without losing it.
510 strcpy(hostName1, host_ret->h_name);
511 if ( (tp = DtStrchr(hostName1,'.')) != NULL )
515 * Try comparing again -- avoiding another gethostbyname
518 if ( !strcmp( hostName1,hostName2) )
521 /* restore the dot if necessary */
524 if ( (host_ret = _XGethostbyname(hostName2, host_buf)) == NULL )
525 return False; /* treat them as different on failure */
527 if ( !strcmp(hostName1, host_ret->h_name) )
530 return False; /* The names are different */
534 /******************************************************************************
536 * _DtGetActionIconDefault -
537 * return the default action icon name string based on the "*ActionIcon"
538 * X resource and the DtACTION_ICON_DEFAULT value.
540 *****************************************************************************/
543 _DtGetActionIconDefault ( void )
545 static char *defaultActionIcon = NULL;
546 char nameBuf[_DtAct_MAX_BUF_SIZE];
547 char classBuf[_DtAct_MAX_BUF_SIZE];
548 XrmValue resource_value;
556 if ( defaultActionIcon ) {
557 _DtSvcProcessUnlock();
558 return XtNewString(defaultActionIcon);
561 bytesNeeded = strlen(DtACTION_ICON_RESOURCE_NAME)
562 + strlen(_DtApplicationName) + 4;
563 if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
564 name = XtMalloc(bytesNeeded);
568 sprintf (name, "%s*%s",
569 _DtActNULL_GUARD( _DtApplicationName) , DtACTION_ICON_RESOURCE_NAME);
572 bytesNeeded = strlen(DtACTION_ICON_RESOURCE_CLASS)
573 + strlen(_DtApplicationClass) + 4;
574 if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
575 class = XtMalloc(bytesNeeded);
578 sprintf (class, "%s*%s",
579 _DtActNULL_GUARD(_DtApplicationClass) , DtACTION_ICON_RESOURCE_CLASS);
582 db = XtDatabase (_DtDisplay);
585 if (db && XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
586 defaultActionIcon = (char *) XtNewString (resource_value.addr);
588 defaultActionIcon = (char *) XtNewString (DtACTION_ICON_DEFAULT);
590 if ( name != nameBuf )
592 if ( class != classBuf )
595 _DtSvcProcessUnlock();
596 return XtNewString(defaultActionIcon);
600 /******************************************************************************
602 * _DtGetExecHostsDefault -
603 * Returns the default execution host string based on the "*executionHosts"
604 * X resource and the default vaule of DtEXEC_HOSTS_DEFAULT.
610 *****************************************************************************/
613 _DtGetExecHostsDefault ( void )
615 static char *executionHosts = NULL;
616 char nameBuf[_DtAct_MAX_BUF_SIZE];
617 char classBuf[_DtAct_MAX_BUF_SIZE];
618 XrmValue resource_value;
625 if ( executionHosts ) {
626 _DtSvcProcessUnlock();
627 return XtNewString(executionHosts);
630 bytesNeeded = strlen(DtEXEC_HOSTS_NAME) + strlen(_DtApplicationName) + 4;
632 if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
633 name = XtMalloc(bytesNeeded);
636 sprintf (name, "%s*%s",
637 _DtActNULL_GUARD(_DtApplicationName), DtEXEC_HOSTS_NAME);
640 bytesNeeded = strlen(DtEXEC_HOSTS_CLASS) + strlen(_DtApplicationClass) + 4;
641 if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
642 class = XtMalloc(bytesNeeded);
645 sprintf (class, "%s*%s",
646 _DtActNULL_GUARD(_DtApplicationClass), DtEXEC_HOSTS_CLASS);
648 db = XtDatabase (_DtDisplay);
649 if (db && XrmGetResource (db, name, class, &rep_type, &resource_value))
650 executionHosts = (char *) XtNewString (resource_value.addr);
652 executionHosts = (char *) XtNewString (DtEXEC_HOSTS_DEFAULT);
654 if ( name != nameBuf )
656 if ( class != classBuf )
659 _DtSvcProcessUnlock();
660 return XtNewString(executionHosts);
664 /******************************************************************************
665 char *_DtGetDtTmpDir()
667 check resource; then go to internal default for value of Dt Tmp
668 directory path. This function returns a newly allocated string, it is
669 up to the caller to free it.
671 *****************************************************************************/
672 char *_DtGetDtTmpDir(void)
674 static char *DtTmpDirPath = NULL;
676 char nameBuf[_DtAct_MAX_BUF_SIZE];
677 char classBuf[_DtAct_MAX_BUF_SIZE];
682 XrmValue resource_value;
686 if ( DtTmpDirPath ) {
687 _DtSvcProcessUnlock();
688 return XtNewString(DtTmpDirPath);
692 * Check if a resource has been set for the tmp dir location
695 bytesNeeded = strlen(DtACTION_DTTMPDIR_RESOURCE_NAME)
696 + strlen(_DtApplicationName) + 4;
697 if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
698 name = XtMalloc(bytesNeeded);
702 sprintf (name, "%s*%s",
703 _DtActNULL_GUARD( _DtApplicationName) , DtACTION_DTTMPDIR_RESOURCE_NAME);
706 bytesNeeded = strlen(DtACTION_DTTMPDIR_RESOURCE_CLASS)
707 + strlen(_DtApplicationClass) + 4;
708 if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
709 class = XtMalloc(bytesNeeded);
712 sprintf (class, "%s*%s",
713 _DtActNULL_GUARD(_DtApplicationClass) , DtACTION_DTTMPDIR_RESOURCE_CLASS);
715 db = XtDatabase (_DtDisplay);
716 if (db && XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
717 DtTmpDirPath = (char *) XtNewString (resource_value.addr);
720 /* RWV: is this the right HOME if we've changed user id? */
721 char *home = getenv("HOME");
722 dirBuf = XtCalloc(1, MAXPATHLEN + 1);
723 snprintf(dirBuf, MAXPATHLEN, "%s/%s", home, DtACTION_DTTMPDIR_DEFAULT);
724 DtTmpDirPath = XtNewString(dirBuf);
729 * Save a copy of the path for future reference
731 _DtSvcProcessUnlock();
732 return XtNewString(DtTmpDirPath);
735 /*****************************************************************************
737 * _DtActGenerateTmpFile(char *dir, char *format)
738 * Generate a temporary file in the directory and format specified.
739 * format is assumed to contain a single %s and to be in a form
740 * suitable for use by sprintf(). If "dir" is not accessable or NULL
741 * then the default CDE tmp dir, the contents of the "TMPDIR" environment
742 * variable, the P_tmpdir defined in stdio.h and finally "/tmp" are
744 * This function returns a newly malloc-ed string containing a
745 * full path for a new tmp file. The open file descriptor for the tmp
746 * file is returned through an int pointer (*fd).
748 * If no unused tmp name can be generated (within 1000 tries) then this
749 * function returns NULL.
751 *****************************************************************************/
754 _DtActGenerateTmpFile(char *dir,char *format,mode_t mode,int *fd)
757 static unsigned long nameCount = 0xA;
759 int countTrys = 0; /* count of the number of tmp names tried */
760 char nameBuf[MAXPATHLEN];
772 d = _DtGetDtTmpDir();
778 * Make sure the desired directory is avaliable
779 * if not try P_tmpdir (i.e. /usr/tmp), finally resort
780 * to "/tmp" if no other tmp dir can be accessed.
782 if ( stat(d,&statbuf) )
785 * The passed in directory cannot be accessed so
786 * try some alternatives.
789 static char *AltTmpDirs[] = {
790 NULL, /* reserved for getenv() */
791 P_tmpdir, /* from stdio.h */
797 AltTmpDirs[0] = getenv("TMPDIR");
806 * Because "free_d" is false (i.e.0) at this point any
807 * pointer assigned to d from the static array AltTmpDirs
808 * is protected from being erroneously freed at the
809 * end of this function.
811 myassert(free_d == 0);
813 for ( i= 0; i < sizeof(AltTmpDirs)/sizeof(char *); i++ )
815 if ( !(d = AltTmpDirs[i]) )
817 if ( stat(d,&statbuf) == 0 )
820 _DtSvcProcessUnlock();
824 myassert(0 /* this should never happen */);
833 sprintf(nameBuf,"%lx_%d",nameCount++,pid);
834 _DtSvcProcessUnlock();
835 base = XtNewString(nameBuf);
837 * Convert the base name to the desired format
839 sprintf(nameBuf,f,base);
843 * If the format string does not allow for variation
844 * of the tmp file name (i.e. no %s in format) then
845 * there is no sense trying more than once.
847 if ( countTrys > 0 && (strcmp(f,nameBuf) == 0))
850 base = XtNewString(nameBuf);
853 * generate the full path name to the new tmp file
855 /* if ( d[strlen(d)-1] != '/' ) */
856 if ( *DtPrevChar(d,d + strlen(d)) != '/' )
857 sprintf(nameBuf,"%s/%s",d,base);
859 sprintf(nameBuf,"%s%s",d,base);
864 * Check if such a file already exists.
866 *fd = open(nameBuf,(O_WRONLY | O_CREAT | O_EXCL), mode );
868 while ( *fd == -1 && errno == ENOENT && countTrys++ < 1000 );
874 return NULL; /* unable to generate desired name format */
877 * The file has been successfully created -- return the name for
878 * use by the caller. The already open fd is also available to the
879 * calling function. It is up to the caller to close the file.
882 return XtNewString(nameBuf);
885 /*******************************************************************************
886 * _DtRemoveTrailingBlanksInPlace
887 * Removes trailing white space from the passed string. The string
888 * is modified in place.
889 ******************************************************************************/
891 _DtRemoveTrailingBlanksInPlace(char **s)
895 if (!s || !strlen(*s))
898 for ( p = DtPrevChar(*s,*s + strlen(*s));
899 DtIsspace(p) && (p > *s);
906 /******************************************************************************
908 * _DtExecuteAccess ( path )
911 * char *path; // path of potentially executable file
913 * For effective user id,
914 * RETURNS: True (1) if file is executable;
915 * False (0)if file is not executable;
916 * -1 if the file cannot be accessed.
918 *****************************************************************************/
922 #define NGROUPS_MAX_VALUE NGROUPS_UMAX
924 #define NGROUPS_MAX_VALUE NGROUPS_MAX
928 _DtExecuteAccess( const char *path )
934 gid_t groupids[NGROUPS_MAX_VALUE];
936 _Xgetgrparams grp_buf;
938 if (stat( path, &s ) == -1 ) {
939 /* could not stat file, no access */
945 if( (S_IXUSR & s.st_mode) == S_IXUSR ) {
946 if(euid == s.st_uid || euid == 0) {
947 /* execution permitted for user */
951 if (euid == s.st_uid && euid != 0) {
952 /* user execution not permitted */
957 if( (S_IXGRP & s.st_mode) == S_IXGRP ) {
958 if(getegid() == s.st_gid || euid == 0) {
959 /* execution permitted for group (or superuser) */
963 i = getgroups(getgroups(0,groupids), groupids);
966 for (pgid = groupids; i--; pgid++) {
967 if ((gr = _XGetgrgid(*pgid, grp_buf)) != NULL)
968 if (gr->gr_gid == s.st_gid) {
969 /* execution permitted to group list */
976 if( (S_IXOTH & s.st_mode) == S_IXOTH ) {
977 /* execution permitted for "others" */
986 /******************************************************************************
988 * Routines to manipulate DtActionInvocationID's.
991 /**************************************************
993 * Allocate an unused DtActionInvocationID
996 DtActionInvocationID _DtActAllocID(void)
998 extern _DtActInvRecT **_DtActInvRecArray; /* global */
999 extern int _actInvRecArraySize; /* global */
1001 static unsigned long lastIdWas = 100; /* 0..99 for errors */
1005 _DtSvcProcessLock();
1010 * Need to track down better define than INT_MAX to determine cap
1012 if ( lastIdWas == INT_MAX )
1018 * Verify that the ID is not being used already
1020 for ( i = 0 ; i < _actInvRecArraySize ; i++ ) {
1021 if ( _DtActInvRecArray[i] ) {
1022 if ( (_DtActInvRecArray[i] -> id) == lastIdWas )
1026 } while ( ! found );
1028 _DtSvcProcessUnlock();
1029 return( lastIdWas );
1033 /**************************************************
1035 * Allocate an Invocation Record
1037 _DtActInvRecT *_DtActAllocInvRec(void)
1039 extern _DtActInvRecT **_DtActInvRecArray; /* global */
1040 extern int _actInvRecArraySize; /* global */
1043 static int first_time = 1;
1046 _DtSvcProcessLock();
1048 * If first time, malloc array of InvRec pointers
1051 _actInvRecArraySize = _START_INVREC_SIZE;
1052 _DtActInvRecArray = (_DtActInvRecT **) XtMalloc(sizeof(_DtActInvRecT *)
1053 * _actInvRecArraySize);
1056 * A NULL indicates an available slot.
1058 for ( i = 0; i < _actInvRecArraySize; i++ )
1059 _DtActInvRecArray[i] = NULL;
1065 * Look through existing list of InvRec's for an available slot.
1068 for ( i = 0; i < _actInvRecArraySize; i++ ) {
1069 if ( _DtActInvRecArray[i] == NULL ) {
1075 if ( newslot == -1 ) {
1077 * Need to grow InvRecArray since current one is full.
1079 _actInvRecArraySize += 10;
1080 _DtActInvRecArray = (_DtActInvRecT **)
1081 XtRealloc( (char *) _DtActInvRecArray,
1082 sizeof(_DtActInvRecT *)
1083 * _actInvRecArraySize );
1086 * NULL out new entries
1088 for ( i = _actInvRecArraySize-10; i < _actInvRecArraySize; i++ )
1089 _DtActInvRecArray[i] = NULL;
1091 newslot = _actInvRecArraySize-10;
1095 * Hang a new InvRec off the array and initialize all to zero.
1097 _DtActInvRecArray[newslot] = (_DtActInvRecT *)
1098 XtCalloc(1, sizeof(_DtActInvRecT) );
1099 _DtActInvRecArray[newslot]->id = _DtActAllocID();
1100 _DtSvcProcessUnlock();
1102 return( _DtActInvRecArray[newslot] );
1105 /******************************************************************************
1107 * _DtActFreeChildRec()
1109 * Completely free the contents of, and free the existence of a childRec.
1111 *****************************************************************************/
1114 _DtActFreeChildRec( _DtActChildRecT *childRecP)
1119 XtFree((char *)childRecP->argMap);
1121 if ( IS_CMD( childRecP->mask ) ) {
1122 XtFree((char *) childRecP->u.cmd.TTProcId);
1124 if (childRecP->u.cmd.reqMessage) {
1125 data = (CallbackData *)
1126 tt_message_user(childRecP->u.cmd.reqMessage,0);
1129 XtFree((char *) data->actionLabel);
1132 * tjg: question if we should do this
1134 * if (data->actionPtr)
1135 * _DtFreeActionStruct( data->actionPtr );
1138 if (data->requestPtr)
1139 _DtFreeRequest(data->requestPtr);
1141 XtFree((char *) data);
1144 tttk_message_destroy(childRecP->u.cmd.reqMessage);
1147 else if ( IS_TT_MSG( childRecP->mask ) ) {
1148 tt_free((char *) childRecP->u.tt.TTProcId);
1150 if (childRecP->u.tt.reqMessage) {
1151 data = (CallbackData *)
1152 tt_message_user(childRecP->u.tt.reqMessage,0);
1155 XtFree((char *) data->actionLabel);
1158 * tjg: question if we should do this
1160 * if (data->actionPtr)
1161 * _DtFreeActionStruct( data->actionPtr );
1164 if (data->requestPtr)
1165 _DtFreeRequest(data->requestPtr);
1167 XtFree((char *) data);
1169 tttk_message_destroy(childRecP->u.tt.reqMessage);
1173 XtFree( (char *) childRecP );
1176 /******************************************************************************
1178 * _DtActDeleteChildRec()
1180 * Within an existing invRec, delete one of its children. Squeeze the
1181 * childRec array if needed (but don't bother to realloc smaller).
1183 * Returns 1 for successful deletion.
1184 * Returns 0 if unable to delete the record.
1186 ******************************************************************************/
1187 int _DtActDeleteChildRec( _DtActInvRecT *invp, _DtActChildRecT *childp)
1191 if ( !invp || !childp )
1194 for ( i = 0; i < invp->numChildren; i++ )
1196 if ( invp->childRec[i] == childp )
1198 _DtActFreeChildRec(childp);
1199 invp->numChildren--;
1200 invp->childRec[i] = NULL;
1202 * Close the potential gap created in the array.
1204 for ( j = i; j < invp->numChildren; j++)
1206 invp->childRec[i] = invp->childRec[i+1];
1207 invp->childRec[i+1] = NULL;
1212 /* child not found */
1216 /******************************************************************************
1218 * _DtActDeleteInvRec( id )
1219 * Delete an Action Invocation Record given an invocation id
1220 * Returns 1 for successful deletion.
1221 * Returns 0 if unable to delete the record.
1222 * Returns -1 if the record is not found.
1224 * note: as pointers are free'd, they should be set to NULL
1226 ******************************************************************************/
1227 int _DtActDeleteInvRec( DtActionInvocationID id )
1232 _DtSvcProcessLock();
1233 for ( i = 0; i < _actInvRecArraySize; i++ )
1235 if ( _DtActInvRecArray[i] )
1237 if ( (_DtActInvRecArray[i]->id) == id )
1239 _DtActInvRecT *invp = _DtActInvRecArray[i];
1240 _DtActArgInfo *infop;
1244 * Check for any tmp files created to house buffers
1245 * Delete them if they still exist.
1248 for ( j = 0; j < invp->ac; j++)
1250 infop = &invp->info[j];
1252 if (IS_BUFFER_OBJ(infop->mask) && IS_FILE_OBJ(infop->mask))
1254 myassert((infop->name != NULL) && (*infop->name != '\0'));
1258 * These tmp names should have been created by
1259 * the execution management code and such names
1260 * should not refer to directories. We will not
1261 * check that assertion here.
1263 if ( !IS_WRITE_OBJ(infop->mask) )
1265 mode_t mode = ( S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP);
1267 * Change the file's mode before deleting
1269 chmod(infop->name,mode);
1271 (void)unlink(infop->name);
1272 RESET_FILE_OBJ(infop->mask);
1278 * Cleanup invocation record. Don't worry about squeezing
1279 * and realloc'ing the _DtActInvRecArray.
1281 _DtActFreeInvRec( _DtActInvRecArray[i] );
1282 _DtActInvRecArray[i] = NULL;
1283 _DtSvcProcessUnlock();
1284 return 1; /* successfully deleted */
1289 _DtSvcProcessUnlock();
1290 return -1; /* not found */
1293 /**************************************************
1295 * Allocate a Child Record; hang off parent
1297 _DtActChildRecT *_DtActAllocChildRec( _DtActInvRecT *invRec )
1299 _DtActChildRecT *tchildRec;
1303 (invRec->numChildren)++;
1304 if ( invRec->numChildren == 1 ) {
1306 * First child. Build array.
1308 invRec->childRec = (_DtActChildRecT **)
1309 XtMalloc( sizeof(_DtActChildRecT *) );
1312 invRec->childRec = (_DtActChildRecT **)
1313 XtRealloc( (char *) invRec->childRec,
1314 sizeof(_DtActChildRecT *) *
1315 invRec->numChildren);
1319 * Hang a child rec off and initialize
1321 invRec->childRec[invRec->numChildren - 1] = (_DtActChildRecT *)
1322 XtMalloc( sizeof(_DtActChildRecT) );
1324 tchildRec = invRec->childRec[invRec->numChildren - 1]; /* shorthand */
1326 memset(tchildRec, 0, sizeof(_DtActChildRecT));
1327 tchildRec->childId = invRec->numChildren; /* serial # of sorts */
1329 /* tchildRec->u.* = initialized elsewhere */
1331 return( (_DtActChildRecT *) tchildRec );
1334 return( (_DtActChildRecT *) NULL ); /* should not happen */
1339 /**************************************************
1341 * Given an ID, find a DtActInvRec.
1343 _DtActInvRecT *_DtActFindInvRec( DtActionInvocationID id )
1345 extern _DtActInvRecT **_DtActInvRecArray;
1346 extern int _actInvRecArraySize;
1350 _DtSvcProcessLock();
1351 for ( i = 0; i < _actInvRecArraySize; i++ ) {
1352 if ( _DtActInvRecArray[i] ) {
1353 if ( (_DtActInvRecArray[i]->id) == id ) {
1354 _DtSvcProcessUnlock();
1355 return( _DtActInvRecArray[i] ); /* match */
1360 _DtSvcProcessUnlock();
1361 return( (_DtActInvRecT *) NULL ); /* no match */
1364 /**************************************************
1366 * Given an ID and a childId, find a DtActChildRec.
1368 _DtActChildRecT *_DtActFindChildRec(
1369 DtActionInvocationID id,
1370 unsigned long childId)
1373 _DtActInvRecT *invRec;
1376 if ( (invRec = _DtActFindInvRec( id )) == NULL )
1377 return( (_DtActChildRecT *) NULL ); /* no match */
1379 for ( i = 0; i < invRec->numChildren; i++ ) {
1380 if ( invRec->childRec[i] ) {
1381 if ( (invRec->childRec[i]->childId) == childId ) {
1382 return( invRec->childRec[i] ); /* match */
1387 return( (_DtActChildRecT *) NULL ); /* no match */
1391 /******************************************************************************
1393 * _DtActFreeInvRec( p )
1394 * Free a given action invocation record.
1396 *****************************************************************************/
1399 _DtActFreeInvRec( _DtActInvRecT *invp )
1405 * Free info argMap of action arguments.
1408 for ( i=0; i < invp->ac; i++ ) {
1409 XtFree(invp->info[i].type);
1410 XtFree(invp->info[i].name);
1412 XtFree((char *)invp->info);
1418 * Free up cached data if any. This is a rude move since it should
1419 * have been expected to be uploaded by now.
1421 if (invp->cachedUploadCnt) {
1422 for ( i = 0; i < invp->cachedUploadCnt; i++ ) {
1423 _DtActFreeArgArray( invp->cachedUploads[i].newArgp,
1424 invp->cachedUploads[i].newArgc);
1426 XtFree( (char *) invp->cachedUploads );
1430 * Free up child records if necessary.
1432 for ( i = 0; i < invp->numChildren; i++ ) {
1436 _DtActFreeChildRec( invp->childRec[i] );
1438 XtFree( (char *) invp->childRec );
1440 XtFree((char *)invp);
1443 /******************************************************************************
1445 * Add an update to the invocation upload cache.
1447 static void _DtActCacheArgs(
1448 _DtActInvRecT *invRecP,
1449 DtActionArg *newArgp,
1451 DtActionStatus userStatus )
1455 * Grow the cache. We'll add to it one by one, but when a cache
1456 * flush happens later, all entries will go and be freed.
1458 if ( invRecP->cachedUploadCnt )
1459 invRecP->cachedUploads = (_DtActUpdateCache *)
1460 XtRealloc( (char *) invRecP->cachedUploads,
1461 (invRecP->cachedUploadCnt+1) *
1462 sizeof(_DtActUpdateCache) );
1464 invRecP->cachedUploads = (_DtActUpdateCache *)
1465 XtMalloc( sizeof(_DtActUpdateCache) );
1467 invRecP->cachedUploads[invRecP->cachedUploadCnt].newArgp = newArgp;
1468 invRecP->cachedUploads[invRecP->cachedUploadCnt].newArgc = newArgc;
1469 invRecP->cachedUploads[invRecP->cachedUploadCnt].userStatus = userStatus;
1471 (invRecP->cachedUploadCnt)++;
1474 /******************************************************************************
1476 * Routine to evaluate the done-ness of an invocation session.
1478 * This is the key evalulation routine that should be used at all
1479 * execution leaf nodes to decide if and how an "invocation session"
1480 * should be shutdown.
1482 *****************************************************************************/
1483 unsigned long _DtActEvalChildren(DtActionInvocationID id)
1485 _DtActInvRecT *invRec;
1486 unsigned long cstats;
1490 invRec = _DtActFindInvRec( id );
1493 if ( IS_INV_FINISHED(invRec->state) ) {
1495 * All children have been launched (FINISHED), or we're done
1496 * launching new children (CANCEL), so now look at the status
1497 * of each child to figure out what to do.
1500 if ( invRec->numChildren ) {
1502 for ( i = 0; i < invRec->numChildren; i++ ) {
1503 cstats |= invRec->childRec[i]->childState;
1508 * The invocation is finished, and yet there are no
1509 * children. Probably the result of a cancel, so
1510 * cobble up a child status of _DtActCHILD_CANCELED.
1512 cstats = _DtActCHILD_CANCELED;
1516 * Return worst case information on child.
1518 if ( cstats & _DtActCHILD_UNKNOWN )
1519 return( _DtActCHILD_UNKNOWN );
1520 else if ( IS_CHILD_PENDING_START(cstats) )
1521 return ( _DtActCHILD_PENDING_START );
1522 else if ( IS_CHILD_ALIVE_UNKOWN( cstats ) )
1523 return ( _DtActCHILD_ALIVE_UNKNOWN );
1524 else if ( IS_CHILD_ALIVE( cstats ) )
1525 return ( _DtActCHILD_ALIVE );
1526 else if ( IS_CHILD_FAILED( cstats ) )
1527 return ( _DtActCHILD_FAILED );
1528 else if ( IS_CHILD_CANCELED( cstats ) )
1529 return ( _DtActCHILD_CANCELED );
1530 else if ( IS_CHILD_DONE( cstats ) )
1531 return ( _DtActCHILD_DONE );
1533 return( _DtActCHILD_UNKNOWN );
1537 * Not all the children have been launched, so no need to do
1540 return( _DtActCHILD_UNKNOWN );
1544 /******************************************************************************
1546 * _DtActExecutionLeafNodeCleanup()
1548 * At natural execution path leafs within the DtAction code,
1549 * _DtActExecutionLeafNodeCleanup() should be called.
1551 * See rev 1.19 to see how DtActionQuit() functionality was
1552 * once integrated into this routine.
1554 * Basic shutdown process:
1556 * 1. If not in a DtACTION_DONE* state and there are no arguments,
1559 * 2. If not in a DtACTION_DONE* state and there are arguments,
1560 * generate a DtACTION_STATUS_UPDATE with arguments.
1562 * 3. If in a DtACTION_DONE* state with or without arguments,
1563 * generate a DtACTION_DONE* with or without arguments,
1564 * and do a total shutdown of the entire invocation session.
1566 void _DtActExecutionLeafNodeCleanup(
1567 DtActionInvocationID id,
1568 DtActionArg *newArgp,
1570 int respectQuitBlock )
1572 unsigned long evalStatus;
1573 DtActionStatus userStatus;
1574 _DtActInvRecT *invRecP;
1575 int flushCache, useCache;
1578 flushCache = 0; /* flush the cache 1st */
1579 useCache = 0; /* must cache */
1581 invRecP = _DtActFindInvRec( id );
1584 * The whole invocation session is already down.
1586 * One possible way that this can happen is a fast
1587 * tt_reply that triggers a DtACTION_DONE *before*
1588 * the _DtActTimerCB() routine can go off.
1590 * Free any args since they won't be going anywhere.
1592 _DtActFreeArgArray( newArgp, newArgc );
1596 if ( !IS_INV_ID_RETURNED(invRecP->state) )
1597 useCache = 1; /* Need to cache */
1598 else if (invRecP->cachedUploadCnt)
1599 flushCache = 1; /* Can do uploads 1st, if any */
1602 * See if we still need to generate the DtACTION_INVOKED
1605 if (IS_INV_FINISHED(invRecP->state)) {
1606 if ( IS_INV_INDICATOR_ON(invRecP->state) )
1608 /* Turn off the activity indicator */
1609 _DtSendActivityDoneNotification();
1610 RESET_INV_INDICATOR_ON(invRecP->state);
1612 if ( CALL_INV_CB(invRecP->state) ) {
1613 if ( invRecP->cb ) {
1614 (invRecP->cb)( id, invRecP->client_data,
1615 (DtActionArg *) NULL, 0,
1618 SET_INV_CB_CALLED(invRecP->state);
1623 * See if we have any cache stuff to flush
1626 for ( i = 0; i < invRecP->cachedUploadCnt; i++ ) {
1628 (invRecP->cb)( id, invRecP->client_data,
1629 invRecP->cachedUploads[i].newArgp,
1630 invRecP->cachedUploads[i].newArgc,
1631 invRecP->cachedUploads[i].userStatus );
1635 * We normally won't have cached data if no cb.
1637 _DtActFreeArgArray( invRecP->cachedUploads[i].newArgp,
1638 invRecP->cachedUploads[i].newArgc);
1642 XtFree( (char *) invRecP->cachedUploads );
1643 invRecP->cachedUploadCnt = 0;
1649 * If 'newArgp' is nothing but DtACTION_NULLARGS, free it and the
1650 * following code will respond correctly.
1652 * If the user did not register a callback, then free 'newArgp'
1653 * since it won't be going anywhere.
1655 if ( !(invRecP->cb) ) {
1656 _DtActFreeArgArray( newArgp, newArgc );
1661 for ( i = 0; i < newArgc; i++ ) {
1662 if ( newArgp[i].argClass != DtACTION_NULLARG ) {
1666 if ( i == newArgc ) {
1667 _DtActFreeArgArray( newArgp, newArgc );
1675 * See what the overall invocation session status is given the
1676 * child's possible status change.
1678 evalStatus = _DtActEvalChildren(id);
1679 switch (evalStatus) {
1680 case _DtActCHILD_DONE:
1681 userStatus = DtACTION_DONE;
1683 case _DtActCHILD_CANCELED:
1684 userStatus = DtACTION_CANCELED;
1686 case _DtActCHILD_FAILED:
1687 userStatus = DtACTION_FAILED;
1691 * This is pseudo correct. The setting here just causes us
1692 * to dive into other test conditions.
1694 userStatus = DtACTION_STATUS_UPDATE;
1698 /****************************************************************
1700 * Even though the invocation session appears "done", the
1701 * following test case may convert us into doing just
1704 if ( (useCache) && (userStatus != DtACTION_STATUS_UPDATE) ) {
1706 * The session is done, but we're in a caching situation,
1707 * so convert this "done" status to an "update" status.
1708 * When we get out of the caching situation, another evaluation
1709 * (by way of a XtTimer) will re-generate the "done" status
1712 userStatus = DtACTION_STATUS_UPDATE;
1715 if ( userStatus == DtACTION_STATUS_UPDATE ) {
1717 * Some sort of DtACTION_STATUS_UPDATE state.
1719 * Suppress giving an update if there are no arguments - it's
1720 * a waste of effort.
1722 if ( (newArgc) && (invRecP->cb) ) {
1724 _DtActCacheArgs( invRecP, newArgp, newArgc, userStatus );
1727 (invRecP->cb)( id, invRecP->client_data,
1735 * Some sort of DtACTION_DONE* state
1737 * We are not under a caching condition, so we must handle the
1738 * shutdown here. No matter whether we have new arguments, we
1739 * need to deliver the DtACTION_DONE* status.
1741 if ( invRecP->cb ) {
1743 _DtActCacheArgs( invRecP, newArgp, newArgc, userStatus );
1746 (invRecP->cb)( id, invRecP->client_data,
1753 * Delete the entire invocation session. "The Final Act"!
1755 _DtActDeleteInvRec( id );
1759 /******************************************************************************
1761 * Create an array of N DtACTION_NULLARG's.
1763 DtActionArg *_DtActMallocEmptyArgArray(int ac)
1765 DtActionArg *newArgP;
1771 newArgP = (DtActionArg *) XtCalloc( ac, sizeof(DtActionArg) );
1773 for ( i = 0; i < ac; i++ ) {
1774 newArgP[i].argClass = DtACTION_NULLARG;
1780 /******************************************************************************
1782 * Free an array of N DtActionArg's.
1784 void _DtActFreeArgArray( DtActionArg *argp, int ac )
1789 for ( i = 0; i < ac; i++ ) {
1790 if ( argp[i].argClass == DtACTION_FILE ) {
1791 XtFree( argp[i].u.file.name );
1793 else if ( argp[i].argClass == DtACTION_BUFFER ) {
1794 XtFree( (char *) argp[i].u.buffer.bp );
1795 XtFree( argp[i].u.buffer.type );
1796 XtFree( argp[i].u.buffer.name );
1799 XtFree( (char *) argp );
1803 /*****************************************************************************
1805 * _DtActReadTmpFileToBuffer( fname, sizep )
1807 * Read the contents of the tmp file named: "fname" into a buffer.
1808 * Return a pointer to the buffer and fill in "sizep" with the number
1809 * of bytes consumed by the buffer.
1811 * In case of error return a NULL pointer and zero size.
1813 *****************************************************************************/
1816 _DtActReadTmpFileToBuffer( char *fname, int *sizep )
1819 * Read in the current contents of the temp file into a
1820 * buffer for return to original caller.
1830 /* should never get here */
1831 myassert(fname != NULL);
1836 if ( (fd = open(fname,O_RDONLY)) < 0 )
1838 /* couldn't read tmp file */
1839 /* myassert( fd >= 0 ); */
1844 buf = (char *) XtMalloc(MAX_BUF_SIZE);
1845 for (size=0, space=MAX_BUF_SIZE;
1846 (bytes=read(fd,buf+size,space)) != 0;
1851 myassert(0 /* read error on tmp file */);
1852 break; /* return as much as we got */
1854 if ( (space -= bytes) <= 0 )
1857 * Time to allocate more space
1859 buf= (char *)XtRealloc((char *)buf,size + bytes + MAX_BUF_SIZE);
1860 space = MAX_BUF_SIZE;
1867 return XtRealloc((char *)buf,size);
1870 /*****************************************************************************
1872 * _DtActRetCmdChildArgs(childp, aargv)
1874 * Create an argument vector containing the returnable arguments for
1875 * a command action child. The vector pointer is passed in "aargv"
1876 * space for it should already have been allocated by the caller.
1877 * The number of returned arguments is returned by the function.
1879 *****************************************************************************/
1882 _DtActGetCmdReturnArgs(
1883 DtActionInvocationID invId,
1884 _DtActChildRecT *childp,
1885 DtActionArg **aargv )
1888 _DtActInvRecT *invp;
1889 DtActionArg *newArgp;
1891 if ( !(invp = _DtActFindInvRec(invId)) )
1893 myassert( 0 /* this should never happen */ );
1899 * Allocate enough arguments args for the original invocation
1902 *aargv = newArgp = _DtActMallocEmptyArgArray( invp->ac );
1904 for ( i = 0; i < childp->numObjects; i++ )
1906 _DtActArgInfo *infop;
1909 Idx = childp->argMap[i].argIdx;
1910 infop = &invp->info[Idx];
1912 if ( !IS_WRITE_OBJ(infop->mask) )
1913 continue; /* only return writable objectes */
1915 if ( IS_BUFFER_OBJ( infop->mask) )
1917 if ( IS_FILE_OBJ( infop->mask) )
1919 newArgp[Idx].argClass = DtACTION_BUFFER;
1920 newArgp[Idx].u.buffer.type = XtNewString(infop->type);
1923 * If did the following, the returned buffer name
1924 * may NOT be the same as the name which was passed
1925 * in to us (e.g. we were unable to create a tmp file
1926 * with the desired name). At present there is nothing
1927 * that preseves the original buffer name.
1929 * newArgp[Idx].u.buffer.name = _DtBasename(infop->name);
1931 * instead we'll set the returned name field to NULL; this
1932 * is after all a new copy of the buffer.
1934 newArgp[Idx].u.buffer.name = NULL;
1935 newArgp[Idx].u.buffer.writable = True;
1936 newArgp[Idx].u.buffer.bp =
1937 _DtActReadTmpFileToBuffer( infop->name,
1938 &newArgp[Idx].u.buffer.size );
1943 * We already have the buffer in memory
1944 * -- THIS SHOULD NEVER BE THE CASE FOR COMMAND ACTIONS--
1946 myassert( 0 /* should never get here */ );
1950 else if ( IS_FILE_OBJ( infop->mask) )
1954 * What about file objects passed in with the String qualifier?
1955 * This returns the original string string which may or may not
1956 * have been a file name. Are we OK here?
1958 newArgp[Idx].argClass = DtACTION_FILE;
1959 newArgp[Idx].u.file.name = XtNewString( infop->name );
1961 myassert( 0 /* unsupported object */ );
1968 * Check the command invoker's queued commands for an instance of
1969 * the given invocation id.
1973 _DtCmdCheckQForId(DtActionInvocationID id)
1975 extern Cmd_RequestQueue * _DtCmdGetQueueHead( void );
1976 Cmd_RequestQueue *pNode;
1979 for ( pNode = _DtCmdGetQueueHead();
1980 pNode; pNode = pNode->next )
1982 dp = (CallbackData *)(pNode->success_data);
1983 if ( dp->requestPtr->invocId == id )
1984 return 1; /* found a match */
1987 /* Unable to find matching invocation id in the queue */