/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these librararies and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $TOG: ActionUtil.c /main/14 1999/02/19 13:10:09 mgreess $ */ /*************************************<+>************************************* ***************************************************************************** ** ** File: ActionUtil.c ** ** Project: DT ** ** Description: This file contains the action library utility source code. ** ** (c) Copyright 1993, 1994 Hewlett-Packard Company ** (c) Copyright 1993, 1994 International Business Machines Corp. ** (c) Copyright 1993, 1994 Sun Microsystems, Inc. ** (c) Copyright 1993, 1994 Novell, Inc. **************************************************************************** ************************************<+>*************************************/ /*LINTLIBRARY*/ #include #include #include #include #include #include /* for *MAX* macros */ #define X_INCLUDE_NETDB_H #define X_INCLUDE_GRP_H #define XOS_USE_XT_LOCKING #include #ifdef _SUN_OS /* Need this for the strtod () call */ #include #endif /* _SUN_OS */ #include #include #include #include /* for DisplayString */ #include /* for X resource defs */ #include /* for X toolkit functions */ #include
#include
#include
#include
#include "myassertP.h" #include "DtSvcLock.h" /****************************************************************************** * * List of active DtActionInvoke() DtActionInvocationID's * *****************************************************************************/ static _DtActInvRecT **_DtActInvRecArray; /* top of array */ static int _actInvRecArraySize; /* size of array */ static void _DtActFreeInvRec(); /* free an inv rec */ static void _DtActFreeChildRec( _DtActChildRecT *childp); #ifndef NDEBUG /* I want to exercise the realloc code for now */ #define _START_INVREC_SIZE 1 /* initial array size */ #else #define _START_INVREC_SIZE 32 /* initial array size */ #endif /* NDEBUG */ #ifndef P_tmpdir #define P_tmpdir "/var/tmp" #endif /******************************************************************************* * _DtBasename -- utiltiy function to return a pointer to a * string containing the basename of a file name. It doesn't * modifiy the original string parameter. If the original file * path ends in "/" the last component of the path is returned as * the basename. ******************************************************************************/ char * _DtBasename( const char *s ) { char *basep; char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1]; char *p = buf; if (!s) return NULL; /* Work on a local copy of the original string */ *p = '\0'; (void)strcpy(p,s); #ifdef _Dt_HOST_COLON_PATH_SUPPORT /* Chop off the "host:" if necessary */ if ( basep = DtStrchr(p,'/') ) { /* if ( basep > p && (*(basep - 1) == ':') ) */ if ( basep > p && ( *DtPrevChar(p,basep) == ':') ) p = basep; } #endif /* _Dt_HOST_COLON_PATH_SUPPORT */ if ( (basep = DtStrrchr(p,'/')) == NULL ) return XtNewString(p); if (( basep == p) && (strlen(basep) == 1)) return XtNewString("/"); /* * check for trailing slash */ /* while (basep == p+strlen(p)-1 ) */ while (basep == DtPrevChar(p,p+strlen(p)) ) { myassert( *basep == '/'); if ( basep == p ) return XtNewString(basep); *basep = '\0'; /* replace trailing slash */ if ( (basep = DtStrrchr(p,'/')) == NULL ) return XtNewString(p); } /* skip past '/' before returning basename */ basep++; return XtNewString(basep); } /******************************************************************************* * _DtPathString -- returns the path portion of the "host:/path" string * passed in as a parameter. * The incomming string is assumed to be in : "[host:]/path format. * NOTE: a FULL path name is required. ******************************************************************************/ char * _DtPathname( const char *s) { char *slashp; if ( !s ) { myassert(0); return NULL; } /* Chop off the "host:" if necessary */ slashp = DtStrchr((char *)s,'/'); /* if ( slashp > s && (*(slashp - 1) != ':') ) */ if ( slashp > s && (*DtPrevChar(s,slashp) != ':') ) { /* * full path name required * --- should never get here */ myassert(0); return NULL; } return XtNewString(slashp); } /******************************************************************************* * _DtDirname -- returns the directory portion of the file * path string passed in as a parameter. The original string * may be modified to remove trailing slashes. * The incomming string is assumed to be in : "[host:/]dir/file" format. ******************************************************************************/ char * _DtDirname( const char *s) { char *slashp; char *dirp; char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1]; char *p = buf;; /* Work on a local copy of the original string */ *p = '\0'; (void)strcpy(p,s); #ifdef _Dt_HOST_COLON_PATH_SUPPORT /* Chop off the "host:" if necessary */ if ( slashp = DtStrchr(p,'/') ) { /* if ( slashp > p && (*(slashp - 1) == ':') ) */ if ( slashp > p && (*DtPrevChar(p,slashp) == ':') ) p = slashp; } #endif /* _Dt_HOST_COLON_PATH_SUPPORT */ /* handle multiple trailing slashes */ while ( (slashp = DtStrrchr(p,'/')) ) { /* * Special case '/' -- return '/' */ if ( slashp == p ) return XtNewString("/"); /* * Is this a trailing slash ? * -- then try again else break */ if ( slashp == DtPrevChar(p,p + strlen(p)) ) *slashp = '\0'; else break; } /* malformed path */ if (!slashp ) return NULL; /* * Replace the last '/' with a NULL to get the * directory name. */ dirp = XtNewString(p); *(dirp + (slashp - p)) = '\0'; return dirp; } /***************************************************************************** * * _DtHostString * Extract the host name string from files in the [host:][/][path/]file * format. This function mallocs space for a new copy of the host * name string; it is up to the caller to free this space. * This function requires that names which include the hoststring * use a "full path" name to specify the file location. If the hoststring * is omitted the path may be relative. * * RETURN * If a host name string can be found; a pointer to a newly * malloced copy of the host name string is returned; otherwise * a NULL pointer is returned. * * NOTE: This function should NOT be used to extract host name strings * from display variables. * *****************************************************************************/ char * _DtHostString( const char *s) { char *slashp; char *host; char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1]; char *p= buf; if ( (slashp = DtStrchr((char *)s,'/')) == NULL ) return NULL; /* * Make a local copy of the string to avoid problems modifying * "const" strings. */ *p = '\0'; (void) strcpy(p,s); /* if ( (slashp > s) && (*(slashp -1) == ':' )) */ if ( (slashp > s) && (*DtPrevChar(s,slashp) == ':' )) { /* *(p + (slashp - s - 1)) = NULL; */ *(p + (DtPrevChar(s,slashp) - s)) = '\0'; host = XtNewString(p); return host; } return NULL; } char * _DtGetSessionHostName( void ) { static char *sessionHostName = NULL; _DtSvcProcessLock(); if ( sessionHostName && *sessionHostName ) { _DtSvcProcessUnlock(); return XtNewString(sessionHostName); } sessionHostName = getenv(ENV_SESSION_SVR); if ( sessionHostName && *sessionHostName ) { _DtSvcProcessUnlock(); return XtNewString(sessionHostName); } /* * Default to display host name if the session host environment * variable is not set or set to null. The command invoker * initialization should have tucked away the display name. */ sessionHostName = _DtGetDisplayHostName( (Display *) NULL ); _DtSvcProcessUnlock(); return XtNewString(sessionHostName); } char * _DtGetDisplayHostName( Display *dp) { static char *displayHostName = NULL; char *tmpName = NULL; char *tmp; _DtSvcProcessLock(); if ( displayHostName && *displayHostName ) { _DtSvcProcessUnlock(); return XtNewString(displayHostName); } tmpName = XtMalloc(MAXHOSTNAMELEN + 5); tmpName[0] = '\0'; if ( dp ) { /* * If a display pointer has been provided use it to determine * display host name. */ strcpy(tmpName,DisplayString(dp)); if ( tmp = DtStrrchr(tmpName,':') ) { *tmp = '\0'; displayHostName = XtNewString(tmpName); } } else { /* * As a last resort, * In the absence of a display pointer, use the * DISPLAY environment variable. */ strcpy(tmpName,getenv("DISPLAY")); if ( tmp = DtStrrchr(tmpName,':') ) { *tmp = '\0'; displayHostName = XtNewString(tmpName); } } XtFree(tmpName); /* * Check for degenerate forms of the display name */ if ( !( displayHostName && *displayHostName && strcmp(displayHostName,"local") && strcmp(displayHostName, "unix") )) { /* * default to localHostName */ if (displayHostName) XtFree(displayHostName); displayHostName = _DtGetLocalHostName(); } myassert( displayHostName && *displayHostName ); myassert( (DtStrchr( displayHostName, ':' ) == NULL) ); _DtSvcProcessUnlock(); return XtNewString(displayHostName); } /****************************************************************************** * * _DtGetLocalHostname: * return the short form of the local host name. * (i.e. truncate the hostname at the first '.' character). ******************************************************************************/ char * _DtGetLocalHostName( void ) { static char *localHostName = NULL; static char hostNameBuf[MAXHOSTNAMELEN + 1]; char *ptr; _DtSvcProcessLock(); if ( localHostName && *localHostName ) { _DtSvcProcessUnlock(); return XtNewString(localHostName); } if ( gethostname(hostNameBuf, sizeof(hostNameBuf)) ) { _DtSvcProcessUnlock(); return NULL; /* failed gethostname */ } if (ptr = DtStrchr(hostNameBuf, '.')) *ptr = '\0'; /* delete domain name if there is one */ localHostName = hostNameBuf; _DtSvcProcessUnlock(); return XtNewString(localHostName); } /****************************************************************************** * * _DtIsSameHost * return True if the two host names provided are actually references to * the same host; False otherwise. If either host1 or host2 is a NULL * pointer use the "local host" name. * ******************************************************************************/ int _DtIsSameHost(const char *host1, const char *host2) { char hostName1[MAXHOSTNAMELEN + 1]; char hostName2[MAXHOSTNAMELEN + 1]; struct hostent *host_ret; _Xgethostbynameparams host_buf; char *tp; /* * If either parameter is null; use the local host name in its stead */ if ( !host1 ) { tp = _DtGetLocalHostName(); strcpy(hostName1,tp); XtFree(tp); } else { strcpy(hostName1,host1); } if ( !host2) { tp = _DtGetLocalHostName(); strcpy(hostName2,tp); XtFree(tp); } else { strcpy(hostName2,host2); } /* * We now have local copies of the hostnames in the * arrays hostName1 and hostName2. Truncate the names * to their short form before doing the compare. */ if ( (tp = DtStrchr(hostName1,'.')) != NULL ) *tp = '\0'; if ( (tp = DtStrchr(hostName2,'.')) != NULL ) *tp = '\0'; /* * Try to avoid querying the name server (or /etc/hosts). * Do the name strings match? */ if ( !strcmp(hostName1,hostName2) ) return True; if ( (host_ret = _XGethostbyname(hostName1, host_buf)) == NULL ) return False; /* treat them as different on failure */ /* * Save the data from gethostbyname() in "hostName1" so we can * call gethostbyname() again without losing it. */ strcpy(hostName1, host_ret->h_name); if ( (tp = DtStrchr(hostName1,'.')) != NULL ) *tp = '\0'; /* * Try comparing again -- avoiding another gethostbyname * if successful. */ if ( !strcmp( hostName1,hostName2) ) return True; /* restore the dot if necessary */ if ( tp) *tp = '.'; if ( (host_ret = _XGethostbyname(hostName2, host_buf)) == NULL ) return False; /* treat them as different on failure */ if ( !strcmp(hostName1, host_ret->h_name) ) return True; return False; /* The names are different */ } /****************************************************************************** * * _DtGetActionIconDefault - * return the default action icon name string based on the "*ActionIcon" * X resource and the DtACTION_ICON_DEFAULT value. * *****************************************************************************/ char * _DtGetActionIconDefault ( void ) { static char *defaultActionIcon = NULL; char nameBuf[_DtAct_MAX_BUF_SIZE]; char classBuf[_DtAct_MAX_BUF_SIZE]; XrmValue resource_value; XrmDatabase db; char *rep_type; int bytesNeeded; char *name; char *class; _DtSvcProcessLock(); if ( defaultActionIcon ) { _DtSvcProcessUnlock(); return XtNewString(defaultActionIcon); } bytesNeeded = strlen(DtACTION_ICON_RESOURCE_NAME) + strlen(_DtApplicationName) + 4; if ( bytesNeeded > _DtAct_MAX_BUF_SIZE ) name = XtMalloc(bytesNeeded); else name = nameBuf; sprintf (name, "%s*%s", _DtActNULL_GUARD( _DtApplicationName) , DtACTION_ICON_RESOURCE_NAME); bytesNeeded = strlen(DtACTION_ICON_RESOURCE_CLASS) + strlen(_DtApplicationClass) + 4; if ( bytesNeeded > _DtAct_MAX_BUF_SIZE ) class = XtMalloc(bytesNeeded); else class = classBuf; sprintf (class, "%s*%s", _DtActNULL_GUARD(_DtApplicationClass) , DtACTION_ICON_RESOURCE_CLASS); if(_DtDisplay) db = XtDatabase (_DtDisplay); else db = 0; if (db && XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value)) defaultActionIcon = (char *) XtNewString (resource_value.addr); else defaultActionIcon = (char *) XtNewString (DtACTION_ICON_DEFAULT); if ( name != nameBuf ) XtFree(name); if ( class != classBuf ) XtFree(class); _DtSvcProcessUnlock(); return XtNewString(defaultActionIcon); } /****************************************************************************** * * _DtGetExecHostsDefault - * Returns the default execution host string based on the "*executionHosts" * X resource and the default vaule of DtEXEC_HOSTS_DEFAULT. * * PARAMETERS: None. * * RETURNS: char * * *****************************************************************************/ char * _DtGetExecHostsDefault ( void ) { static char *executionHosts = NULL; char nameBuf[_DtAct_MAX_BUF_SIZE]; char classBuf[_DtAct_MAX_BUF_SIZE]; XrmValue resource_value; XrmDatabase db; char *rep_type; char *name, *class; int bytesNeeded; _DtSvcProcessLock(); if ( executionHosts ) { _DtSvcProcessUnlock(); return XtNewString(executionHosts); } bytesNeeded = strlen(DtEXEC_HOSTS_NAME) + strlen(_DtApplicationName) + 4; if ( bytesNeeded > _DtAct_MAX_BUF_SIZE ) name = XtMalloc(bytesNeeded); else name = nameBuf; sprintf (name, "%s*%s", _DtActNULL_GUARD(_DtApplicationName), DtEXEC_HOSTS_NAME); bytesNeeded = strlen(DtEXEC_HOSTS_CLASS) + strlen(_DtApplicationClass) + 4; if ( bytesNeeded > _DtAct_MAX_BUF_SIZE ) class = XtMalloc(bytesNeeded); else class = classBuf; sprintf (class, "%s*%s", _DtActNULL_GUARD(_DtApplicationClass), DtEXEC_HOSTS_CLASS); db = XtDatabase (_DtDisplay); if (db && XrmGetResource (db, name, class, &rep_type, &resource_value)) executionHosts = (char *) XtNewString (resource_value.addr); else executionHosts = (char *) XtNewString (DtEXEC_HOSTS_DEFAULT); if ( name != nameBuf ) XtFree (name); if ( class != classBuf ) XtFree (class); _DtSvcProcessUnlock(); return XtNewString(executionHosts); } /****************************************************************************** char *_DtGetDtTmpDir() check resource; then go to internal default for value of Dt Tmp directory path. This function returns a newly allocated string, it is up to the caller to free it. *****************************************************************************/ char *_DtGetDtTmpDir(void) { static char *DtTmpDirPath = NULL; char *dirBuf = NULL; char nameBuf[_DtAct_MAX_BUF_SIZE]; char classBuf[_DtAct_MAX_BUF_SIZE]; char *name; char *class; int bytesNeeded; char *rep_type; XrmValue resource_value; XrmDatabase db; _DtSvcProcessLock(); if ( DtTmpDirPath ) { _DtSvcProcessUnlock(); return XtNewString(DtTmpDirPath); } /* * Check if a resource has been set for the tmp dir location */ bytesNeeded = strlen(DtACTION_DTTMPDIR_RESOURCE_NAME) + strlen(_DtApplicationName) + 4; if ( bytesNeeded > _DtAct_MAX_BUF_SIZE ) name = XtMalloc(bytesNeeded); else name = nameBuf; sprintf (name, "%s*%s", _DtActNULL_GUARD( _DtApplicationName) , DtACTION_DTTMPDIR_RESOURCE_NAME); bytesNeeded = strlen(DtACTION_DTTMPDIR_RESOURCE_CLASS) + strlen(_DtApplicationClass) + 4; if ( bytesNeeded > _DtAct_MAX_BUF_SIZE ) class = XtMalloc(bytesNeeded); else class = classBuf; sprintf (class, "%s*%s", _DtActNULL_GUARD(_DtApplicationClass) , DtACTION_DTTMPDIR_RESOURCE_CLASS); db = XtDatabase (_DtDisplay); if (db && XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value)) DtTmpDirPath = (char *) XtNewString (resource_value.addr); else { /* RWV: is this the right HOME if we've changed user id? */ dirBuf = XtMalloc(MAXPATHLEN); strcpy(dirBuf,getenv("HOME")); strcat(dirBuf,"/"); strcat(dirBuf,DtACTION_DTTMPDIR_DEFAULT); DtTmpDirPath = XtNewString(dirBuf); XtFree(dirBuf); } /* * Save a copy of the path for future reference */ _DtSvcProcessUnlock(); return XtNewString(DtTmpDirPath); } /***************************************************************************** * * _DtActGenerateTmpFile(char *dir, char *format) * Generate a temporary file in the directory and format specified. * format is assumed to contain a single %s and to be in a form * suitable for use by sprintf(). If "dir" is not accessable or NULL * then the default CDE tmp dir, the contents of the "TMPDIR" environment * variable, the P_tmpdir defined in stdio.h and finally "/tmp" are * tried in turn. * This function returns a newly malloc-ed string containing a * full path for a new tmp file. The open file descriptor for the tmp * file is returned through an int pointer (*fd). * * If no unused tmp name can be generated (within 1000 tries) then this * function returns NULL. * *****************************************************************************/ char * _DtActGenerateTmpFile(char *dir,char *format,mode_t mode,int *fd) { int pid; static unsigned long nameCount = 0xA; int free_d = 0; int countTrys = 0; /* count of the number of tmp names tried */ char nameBuf[MAXPATHLEN]; char *d = dir; char *f = format; char *base; struct stat statbuf; pid = getpid(); if ( !d ) { free_d = 1; d = _DtGetDtTmpDir(); } if ( !f ) f = "%s"; /* * Make sure the desired directory is avaliable * if not try P_tmpdir (i.e. /usr/tmp), finally resort * to "/tmp" if no other tmp dir can be accessed. */ if ( stat(d,&statbuf) ) { /* * The passed in directory cannot be accessed so * try some alternatives. */ int i; static char *AltTmpDirs[] = { NULL, /* reserved for getenv() */ P_tmpdir, /* from stdio.h */ "/tmp", NULL, }; _DtSvcProcessLock(); AltTmpDirs[0] = getenv("TMPDIR"); if ( free_d ) { XtFree(d); free_d = 0; } /* * Because "free_d" is false (i.e.0) at this point any * pointer assigned to d from the static array AltTmpDirs * is protected from being erroneously freed at the * end of this function. */ myassert(free_d == 0); for ( i= 0; i < sizeof(AltTmpDirs)/sizeof(char *); i++ ) { if ( !(d = AltTmpDirs[i]) ) continue; if ( stat(d,&statbuf) == 0 ) break; } _DtSvcProcessUnlock(); if ( !d ) { myassert(0 /* this should never happen */); return NULL; } } do { _DtSvcProcessLock(); sprintf(nameBuf,"%lx_%d",nameCount++,pid); _DtSvcProcessUnlock(); base = XtNewString(nameBuf); /* * Convert the base name to the desired format */ sprintf(nameBuf,f,base); XtFree(base); /* * If the format string does not allow for variation * of the tmp file name (i.e. no %s in format) then * there is no sense trying more than once. */ if ( countTrys > 0 && (strcmp(f,nameBuf) == 0)) return NULL; base = XtNewString(nameBuf); /* * generate the full path name to the new tmp file */ /* if ( d[strlen(d)-1] != '/' ) */ if ( *DtPrevChar(d,d + strlen(d)) != '/' ) sprintf(nameBuf,"%s/%s",d,base); else sprintf(nameBuf,"%s%s",d,base); XtFree(base); /* * Check if such a file already exists. */ *fd = open(nameBuf,(O_WRONLY | O_CREAT | O_EXCL), mode ); } while ( *fd == -1 && errno == ENOENT && countTrys++ < 1000 ); if ( free_d ) XtFree(d); if ( *fd == -1 ) return NULL; /* unable to generate desired name format */ /* * The file has been successfully created -- return the name for * use by the caller. The already open fd is also available to the * calling function. It is up to the caller to close the file. */ return XtNewString(nameBuf); } /******************************************************************************* * _DtRemoveTrailingBlanksInPlace * Removes trailing white space from the passed string. The string * is modified in place. ******************************************************************************/ void _DtRemoveTrailingBlanksInPlace(char **s) { register char *p; if (!s || !strlen(*s)) return; for ( p = DtPrevChar(*s,*s + strlen(*s)); DtIsspace(p) && (p > *s); p=DtPrevChar(*s,p)) *p = '\0'; } /****************************************************************************** * * _DtExecuteAccess ( path ) * * PARAMETERES: * char *path; // path of potentially executable file * * For effective user id, * RETURNS: True (1) if file is executable; * False (0)if file is not executable; * -1 if the file cannot be accessed. * *****************************************************************************/ #ifdef NGROUPS_UMAX #define NGROUPS_MAX_VALUE NGROUPS_UMAX #else #define NGROUPS_MAX_VALUE NGROUPS_MAX #endif int _DtExecuteAccess( const char *path ) { int i, amode, rval; uid_t euid; struct stat s; gid_t *pgid; gid_t groupids[NGROUPS_MAX_VALUE]; struct group *gr; _Xgetgrparams grp_buf; if (stat( path, &s ) == -1 ) { /* could not stat file, no access */ return -1; } euid = geteuid(); if( (S_IXUSR & s.st_mode) == S_IXUSR ) { if(euid == s.st_uid || euid == 0) { /* execution permitted for user */ return 1; } } else { if (euid == s.st_uid && euid != 0) { /* user execution not permitted */ return 0; } } if( (S_IXGRP & s.st_mode) == S_IXGRP ) { if(getegid() == s.st_gid || euid == 0) { /* execution permitted for group (or superuser) */ return 1; } i = getgroups(getgroups(0,groupids), groupids); if ( i > 0) { for (pgid = groupids; i--; pgid++) { if ((gr = _XGetgrgid(*pgid, grp_buf)) != NULL) if (gr->gr_gid == s.st_gid) { /* execution permitted to group list */ return 1; } } } } if( (S_IXOTH & s.st_mode) == S_IXOTH ) { /* execution permitted for "others" */ return 1; } /* no access */ return 0; } /****************************************************************************** * * Routines to manipulate DtActionInvocationID's. */ /************************************************** * * Allocate an unused DtActionInvocationID * between 1..INT_MAX */ DtActionInvocationID _DtActAllocID() { extern _DtActInvRecT **_DtActInvRecArray; /* global */ extern int _actInvRecArraySize; /* global */ static unsigned long lastIdWas = 100; /* 0..99 for errors */ int i, found; _DtSvcProcessLock(); do { found = 1; /* * Need to track down better define than INT_MAX to determine cap */ if ( lastIdWas == INT_MAX ) lastIdWas = 1; else lastIdWas++; /* * Verify that the ID is not being used already */ for ( i = 0 ; i < _actInvRecArraySize ; i++ ) { if ( _DtActInvRecArray[i] ) { if ( (_DtActInvRecArray[i] -> id) == lastIdWas ) found = 0; } } } while ( ! found ); _DtSvcProcessUnlock(); return( lastIdWas ); } /************************************************** * * Allocate an Invocation Record */ _DtActInvRecT *_DtActAllocInvRec() { extern _DtActInvRecT **_DtActInvRecArray; /* global */ extern int _actInvRecArraySize; /* global */ int i, newslot; static int first_time = 1; _DtSvcProcessLock(); /* * If first time, malloc array of InvRec pointers */ if (first_time) { _actInvRecArraySize = _START_INVREC_SIZE; _DtActInvRecArray = (_DtActInvRecT **) XtMalloc(sizeof(_DtActInvRecT *) * _actInvRecArraySize); /* * A NULL indicates an available slot. */ for ( i = 0; i < _actInvRecArraySize; i++ ) _DtActInvRecArray[i] = NULL; first_time = 0; } /* * Look through existing list of InvRec's for an available slot. */ newslot = -1; for ( i = 0; i < _actInvRecArraySize; i++ ) { if ( _DtActInvRecArray[i] == NULL ) { newslot = i; break; } } if ( newslot == -1 ) { /* * Need to grow InvRecArray since current one is full. */ _actInvRecArraySize += 10; _DtActInvRecArray = (_DtActInvRecT **) XtRealloc( (char *) _DtActInvRecArray, sizeof(_DtActInvRecT *) * _actInvRecArraySize ); /* * NULL out new entries */ for ( i = _actInvRecArraySize-10; i < _actInvRecArraySize; i++ ) _DtActInvRecArray[i] = NULL; newslot = _actInvRecArraySize-10; } /* * Hang a new InvRec off the array and initialize all to zero. */ _DtActInvRecArray[newslot] = (_DtActInvRecT *) XtCalloc(1, sizeof(_DtActInvRecT) ); _DtActInvRecArray[newslot]->id = _DtActAllocID(); _DtSvcProcessUnlock(); return( _DtActInvRecArray[newslot] ); } /****************************************************************************** * * _DtActFreeChildRec() * * Completely free the contents of, and free the existance of a childRec. * *****************************************************************************/ static void _DtActFreeChildRec( _DtActChildRecT *childRecP) { CallbackData *data; XtFree((char *)childRecP->argMap); if ( IS_CMD( childRecP->mask ) ) { XtFree((char *) childRecP->u.cmd.TTProcId); if (childRecP->u.cmd.reqMessage) { data = (CallbackData *) tt_message_user(childRecP->u.cmd.reqMessage,0); if (data) { XtFree((char *) data->actionLabel); /* * tjg: question if we should do this * * if (data->actionPtr) * _DtFreeActionStruct( data->actionPtr ); */ if (data->requestPtr) _DtFreeRequest(data->requestPtr); XtFree((char *) data); } tttk_message_destroy(childRecP->u.cmd.reqMessage); } } else if ( IS_TT_MSG( childRecP->mask ) ) { tt_free((char *) childRecP->u.tt.TTProcId); if (childRecP->u.tt.reqMessage) { data = (CallbackData *) tt_message_user(childRecP->u.tt.reqMessage,0); if (data) { XtFree((char *) data->actionLabel); /* * tjg: question if we should do this * * if (data->actionPtr) * _DtFreeActionStruct( data->actionPtr ); */ if (data->requestPtr) _DtFreeRequest(data->requestPtr); XtFree((char *) data); } tttk_message_destroy(childRecP->u.tt.reqMessage); } } XtFree( (char *) childRecP ); } /****************************************************************************** * * _DtActDeleteChildRec() * * Within an existing invRec, delete one of its children. Squeeze the * childRec array if needed (but don't bother to realloc smaller). * * Returns 1 for successful deletion. * Returns 0 if unable to delete the record. * ******************************************************************************/ int _DtActDeleteChildRec( _DtActInvRecT *invp, _DtActChildRecT *childp) { register int i,j; if ( !invp || !childp ) return 0; for ( i = 0; i < invp->numChildren; i++ ) { if ( invp->childRec[i] == childp ) { _DtActFreeChildRec(childp); invp->numChildren--; invp->childRec[i] = NULL; /* * Close the potential gap created in the array. */ for ( j = i; j < invp->numChildren; j++) { invp->childRec[i] = invp->childRec[i+1]; invp->childRec[i+1] = NULL; } return 1; } } /* child not found */ return 0; } /****************************************************************************** * * _DtActDeleteInvRec( id ) * Delete an Action Invocation Record given an invocation id * Returns 1 for successful deletion. * Returns 0 if unable to delete the record. * Returns -1 if the record is not found. * * note: as pointers are free'd, they should be set to NULL * ******************************************************************************/ int _DtActDeleteInvRec( DtActionInvocationID id ) { register int i; CallbackData *data; _DtSvcProcessLock(); for ( i = 0; i < _actInvRecArraySize; i++ ) { if ( _DtActInvRecArray[i] ) { if ( (_DtActInvRecArray[i]->id) == id ) { _DtActInvRecT *invp = _DtActInvRecArray[i]; _DtActArgInfo *infop; int j; /* * Check for any tmp files created to house buffers * Delete them if they still exist. */ for ( j = 0; j < invp->ac; j++) { infop = &invp->info[j]; if (IS_BUFFER_OBJ(infop->mask) && IS_FILE_OBJ(infop->mask)) { myassert((infop->name != NULL) && (*infop->name != '\0')); if ( !infop->name ) continue; /* * These tmp names should have been created by * the execution management code and such names * should not refer to directories. We will not * check that assertion here. */ if ( !IS_WRITE_OBJ(infop->mask) ) { mode_t mode = ( S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP); /* * Change the file's mode before deleting */ chmod(infop->name,mode); } (void)unlink(infop->name); RESET_FILE_OBJ(infop->mask); } } /* * Cleanup invocation record. Don't worry about squeezing * and realloc'ing the _DtActInvRecArray. */ _DtActFreeInvRec( _DtActInvRecArray[i] ); _DtActInvRecArray[i] = NULL; _DtSvcProcessUnlock(); return 1; /* successfully deleted */ } } } _DtSvcProcessUnlock(); return -1; /* not found */ } /************************************************** * * Allocate a Child Record; hang off parent */ _DtActChildRecT *_DtActAllocChildRec( _DtActInvRecT *invRec ) { _DtActChildRecT *tchildRec; if ( invRec ) { (invRec->numChildren)++; if ( invRec->numChildren == 1 ) { /* * First child. Build array. */ invRec->childRec = (_DtActChildRecT **) XtMalloc( sizeof(_DtActChildRecT *) ); } else { invRec->childRec = (_DtActChildRecT **) XtRealloc( (char *) invRec->childRec, sizeof(_DtActChildRecT *) * invRec->numChildren); } /* * Hang a child rec off and initialize */ invRec->childRec[invRec->numChildren - 1] = (_DtActChildRecT *) XtMalloc( sizeof(_DtActChildRecT) ); tchildRec = invRec->childRec[invRec->numChildren - 1]; /* shorthand */ memset(tchildRec, 0, sizeof(_DtActChildRecT)); tchildRec->childId = invRec->numChildren; /* serial # of sorts */ /* tchildRec->u.* = initialized elsewhere */ return( (_DtActChildRecT *) tchildRec ); } else { return( (_DtActChildRecT *) NULL ); /* should not happen */ } } /************************************************** * * Given an ID, find a DtActInvRec. */ _DtActInvRecT *_DtActFindInvRec( DtActionInvocationID id ) { extern _DtActInvRecT **_DtActInvRecArray; extern int _actInvRecArraySize; int i; _DtSvcProcessLock(); for ( i = 0; i < _actInvRecArraySize; i++ ) { if ( _DtActInvRecArray[i] ) { if ( (_DtActInvRecArray[i]->id) == id ) { _DtSvcProcessUnlock(); return( _DtActInvRecArray[i] ); /* match */ } } } _DtSvcProcessUnlock(); return( (_DtActInvRecT *) NULL ); /* no match */ } /************************************************** * * Given an ID and a childId, find a DtActChildRec. */ _DtActChildRecT *_DtActFindChildRec( DtActionInvocationID id, unsigned long childId) { int i; _DtActInvRecT *invRec; if ( (invRec = _DtActFindInvRec( id )) == NULL ) return( (_DtActChildRecT *) NULL ); /* no match */ for ( i = 0; i < invRec->numChildren; i++ ) { if ( invRec->childRec[i] ) { if ( (invRec->childRec[i]->childId) == childId ) { return( invRec->childRec[i] ); /* match */ } } } return( (_DtActChildRecT *) NULL ); /* no match */ } /****************************************************************************** * * _DtActFreeInvRec( p ) * Free a given action invocation record. * *****************************************************************************/ static void _DtActFreeInvRec( _DtActInvRecT *invp ) { register int i; /* * Free info argMap of action arguments. */ if ( invp->info ) { for ( i=0; i < invp->ac; i++ ) { XtFree(invp->info[i].type); XtFree(invp->info[i].name); } XtFree((char *)invp->info); invp->info = NULL; } /* * Free up cached data if any. This is a rude move since it should * have been expected to be uploaded by now. */ if (invp->cachedUploadCnt) { for ( i = 0; i < invp->cachedUploadCnt; i++ ) { _DtActFreeArgArray( invp->cachedUploads[i].newArgp, invp->cachedUploads[i].newArgc); } XtFree( (char *) invp->cachedUploads ); } /* * Free up child records if necessary. */ for ( i = 0; i < invp->numChildren; i++ ) { /* * Remove each child */ _DtActFreeChildRec( invp->childRec[i] ); } XtFree( (char *) invp->childRec ); XtFree((char *)invp); } /****************************************************************************** * * Add an update to the invocation upload cache. */ static void _DtActCacheArgs( _DtActInvRecT *invRecP, DtActionArg *newArgp, int newArgc, DtActionStatus userStatus ) { /* * Grow the cache. We'll add to it one by one, but when a cache * flush happens later, all entries will go and be freed. */ if ( invRecP->cachedUploadCnt ) invRecP->cachedUploads = (_DtActUpdateCache *) XtRealloc( (char *) invRecP->cachedUploads, (invRecP->cachedUploadCnt+1) * sizeof(_DtActUpdateCache) ); else invRecP->cachedUploads = (_DtActUpdateCache *) XtMalloc( sizeof(_DtActUpdateCache) ); invRecP->cachedUploads[invRecP->cachedUploadCnt].newArgp = newArgp; invRecP->cachedUploads[invRecP->cachedUploadCnt].newArgc = newArgc; invRecP->cachedUploads[invRecP->cachedUploadCnt].userStatus = userStatus; (invRecP->cachedUploadCnt)++; } /****************************************************************************** * * Routine to evaluate the done-ness of an invocation session. * * This is the key evalulation routine that should be used at all * execution leaf nodes to decide if and how an "invocation session" * should be shutdown. * *****************************************************************************/ unsigned long _DtActEvalChildren(DtActionInvocationID id) { _DtActInvRecT *invRec; unsigned long cstats; int i; invRec = _DtActFindInvRec( id ); myassert(invRec); if ( IS_INV_FINISHED(invRec->state) ) { /* * All children have been launched (FINISHED), or we're done * launching new children (CANCEL), so now look at the status * of each child to figure out what to do. */ if ( invRec->numChildren ) { cstats = 0; for ( i = 0; i < invRec->numChildren; i++ ) { cstats |= invRec->childRec[i]->childState; } } else { /* * The invocation is finished, and yet there are no * children. Probably the result of a cancel, so * cobble up a child status of _DtActCHILD_CANCELED. */ cstats = _DtActCHILD_CANCELED; } /* * Return worst case information on child. */ if ( cstats & _DtActCHILD_UNKNOWN ) return( _DtActCHILD_UNKNOWN ); else if ( IS_CHILD_PENDING_START(cstats) ) return ( _DtActCHILD_PENDING_START ); else if ( IS_CHILD_ALIVE_UNKOWN( cstats ) ) return ( _DtActCHILD_ALIVE_UNKNOWN ); else if ( IS_CHILD_ALIVE( cstats ) ) return ( _DtActCHILD_ALIVE ); else if ( IS_CHILD_FAILED( cstats ) ) return ( _DtActCHILD_FAILED ); else if ( IS_CHILD_CANCELED( cstats ) ) return ( _DtActCHILD_CANCELED ); else if ( IS_CHILD_DONE( cstats ) ) return ( _DtActCHILD_DONE ); else return( _DtActCHILD_UNKNOWN ); } else { /* * Not all the children have been launched, so no need to do * an analysis. */ return( _DtActCHILD_UNKNOWN ); } } /****************************************************************************** * * _DtActExecutionLeafNodeCleanup() * * At natural execution path leafs within the DtAction code, * _DtActExecutionLeafNodeCleanup() should be called. * * See rev 1.19 to see how DtActionQuit() functionality was * once integrated into this routine. * * Basic shutdown process: * * 1. If not in a DtACTION_DONE* state and there are no arguments, * do nothing. * * 2. If not in a DtACTION_DONE* state and there are arguments, * generate a DtACTION_STATUS_UPDATE with arguments. * * 3. If in a DtACTION_DONE* state with or without arguments, * generate a DtACTION_DONE* with or without arguments, * and do a total shutdown of the entire invocation session. */ void _DtActExecutionLeafNodeCleanup( DtActionInvocationID id, DtActionArg *newArgp, int newArgc, int respectQuitBlock ) { unsigned long evalStatus; DtActionStatus userStatus; _DtActInvRecT *invRecP; int flushCache, useCache; int i; flushCache = 0; /* flush the cache 1st */ useCache = 0; /* must cache */ invRecP = _DtActFindInvRec( id ); if ( !invRecP ) { /* * The whole invocation session is already down. * * One possible way that this can happen is a fast * tt_reply that triggers a DtACTION_DONE *before* * the _DtActTimerCB() routine can go off. * * Free any args since they won't be going anywhere. */ _DtActFreeArgArray( newArgp, newArgc ); return; } if ( !IS_INV_ID_RETURNED(invRecP->state) ) useCache = 1; /* Need to cache */ else if (invRecP->cachedUploadCnt) flushCache = 1; /* Can do uploads 1st, if any */ /* * See if we still need to generate the DtACTION_INVOKED * update. */ if (IS_INV_FINISHED(invRecP->state)) { if ( IS_INV_INDICATOR_ON(invRecP->state) ) { /* Turn off the activity indicator */ _DtSendActivityDoneNotification(); RESET_INV_INDICATOR_ON(invRecP->state); } if ( CALL_INV_CB(invRecP->state) ) { if ( invRecP->cb ) { (invRecP->cb)( id, invRecP->client_data, (DtActionArg *) NULL, 0, DtACTION_INVOKED ); } SET_INV_CB_CALLED(invRecP->state); } } /* * See if we have any cache stuff to flush */ if (flushCache) { for ( i = 0; i < invRecP->cachedUploadCnt; i++ ) { if (invRecP->cb) { (invRecP->cb)( id, invRecP->client_data, invRecP->cachedUploads[i].newArgp, invRecP->cachedUploads[i].newArgc, invRecP->cachedUploads[i].userStatus ); } else { /* * We normally won't have cached data if no cb. */ _DtActFreeArgArray( invRecP->cachedUploads[i].newArgp, invRecP->cachedUploads[i].newArgc); } } XtFree( (char *) invRecP->cachedUploads ); invRecP->cachedUploadCnt = 0; } /* * Data compression * * If 'newArgp' is nothing but DtACTION_NULLARGS, free it and the * following code will respond correctly. * * If the user did not register a callback, then free 'newArgp' * since it won't be going anywhere. */ if ( !(invRecP->cb) ) { _DtActFreeArgArray( newArgp, newArgc ); newArgc = 0; } else { if (newArgp) { for ( i = 0; i < newArgc; i++ ) { if ( newArgp[i].argClass != DtACTION_NULLARG ) { break; } } if ( i == newArgc ) { _DtActFreeArgArray( newArgp, newArgc ); newArgc = 0; newArgp = NULL; } } } /* * See what the overall invocation session status is given the * child's possible status change. */ evalStatus = _DtActEvalChildren(id); switch (evalStatus) { case _DtActCHILD_DONE: userStatus = DtACTION_DONE; break; case _DtActCHILD_CANCELED: userStatus = DtACTION_CANCELED; break; case _DtActCHILD_FAILED: userStatus = DtACTION_FAILED; break; default: /* * This is pseudo correct. The setting here just causes us * to dive into other test conditions. */ userStatus = DtACTION_STATUS_UPDATE; break; } /**************************************************************** * * Even though the invocation session appears "done", the * following test case may convert us into doing just * an "update". */ if ( (useCache) && (userStatus != DtACTION_STATUS_UPDATE) ) { /* * The session is done, but we're in a caching situation, * so convert this "done" status to an "update" status. * When we get out of the caching situation, another evaluation * (by way of a XtTimer) will re-generate the "done" status * for us. */ userStatus = DtACTION_STATUS_UPDATE; } if ( userStatus == DtACTION_STATUS_UPDATE ) { /* * Some sort of DtACTION_STATUS_UPDATE state. * * Suppress giving an update if there are no arguments - it's * a waste of effort. */ if ( (newArgc) && (invRecP->cb) ) { if (useCache) { _DtActCacheArgs( invRecP, newArgp, newArgc, userStatus ); } else { (invRecP->cb)( id, invRecP->client_data, newArgp, newArgc, userStatus ); } } } else { /* * Some sort of DtACTION_DONE* state * * We are not under a caching condition, so we must handle the * shutdown here. No matter whether we have new arguments, we * need to deliver the DtACTION_DONE* status. */ if ( invRecP->cb ) { if (useCache) { _DtActCacheArgs( invRecP, newArgp, newArgc, userStatus ); } else { (invRecP->cb)( id, invRecP->client_data, newArgp, newArgc, userStatus ); } } /* * Delete the entire invocation session. "The Final Act"! */ _DtActDeleteInvRec( id ); } } /****************************************************************************** * * Create an array of N DtACTION_NULLARG's. */ DtActionArg *_DtActMallocEmptyArgArray(int ac) { DtActionArg *newArgP; int i; if ( ac == 0 ) return NULL; newArgP = (DtActionArg *) XtCalloc( ac, sizeof(DtActionArg) ); for ( i = 0; i < ac; i++ ) { newArgP[i].argClass = DtACTION_NULLARG; } return(newArgP); } /****************************************************************************** * * Free an array of N DtActionArg's. */ void _DtActFreeArgArray( DtActionArg *argp, int ac ) { int i; if (argp) { for ( i = 0; i < ac; i++ ) { if ( argp[i].argClass == DtACTION_FILE ) { XtFree( argp[i].u.file.name ); } else if ( argp[i].argClass == DtACTION_BUFFER ) { XtFree( (char *) argp[i].u.buffer.bp ); XtFree( argp[i].u.buffer.type ); XtFree( argp[i].u.buffer.name ); } } XtFree( (char *) argp ); } } /***************************************************************************** * * _DtActReadTmpFileToBuffer( fname, sizep ) * * Read the contents of the tmp file named: "fname" into a buffer. * Return a pointer to the buffer and fill in "sizep" with the number * of bytes consumed by the buffer. * * In case of error return a NULL pointer and zero size. * *****************************************************************************/ void * _DtActReadTmpFileToBuffer( char *fname, int *sizep ) { /* * Read in the current contents of the temp file into a * buffer for return to original caller. */ int fd; int bytes; int size; int space; char *buf; if ( !fname ) { /* should never get here */ myassert(fname != NULL); *sizep = 0; return NULL; } if ( (fd = open(fname,O_RDONLY)) < 0 ) { /* couldn't read tmp file */ /* myassert( fd >= 0 ); */ *sizep = 0; return NULL; } buf = (char *) XtMalloc(MAX_BUF_SIZE); for (size=0, space=MAX_BUF_SIZE; (bytes=read(fd,buf+size,space)) != 0; size += bytes ) { if ( bytes < 0 ) { myassert(0 /* read error on tmp file */); break; /* return as much as we got */ } if ( (space -= bytes) <= 0 ) { /* * Time to allocate more space */ buf= (char *)XtRealloc((char *)buf,size + bytes + MAX_BUF_SIZE); space = MAX_BUF_SIZE; } } close(fd); *sizep = size; return XtRealloc((char *)buf,size); } /***************************************************************************** * * _DtActRetCmdChildArgs(childp, aargv) * * Create an argment vector containing the returnable arguments for * a command action child. The vector pointer is passed in "aargv" * space for it should already have been allocated by the caller. * The number of returned arguments is returned by the function. * *****************************************************************************/ _DtActGetCmdReturnArgs( DtActionInvocationID invId, _DtActChildRecT *childp, DtActionArg **aargv ) { register int i; _DtActInvRecT *invp; DtActionArg *newArgp; if ( !(invp = _DtActFindInvRec(invId)) ) { myassert( 0 /* this should never happen */ ); *aargv = NULL; return 0; } /* * Allocate enough arguments args for the original invocation */ *aargv = newArgp = _DtActMallocEmptyArgArray( invp->ac ); for ( i = 0; i < childp->numObjects; i++ ) { _DtActArgInfo *infop; int Idx; Idx = childp->argMap[i].argIdx; infop = &invp->info[Idx]; if ( !IS_WRITE_OBJ(infop->mask) ) continue; /* only return writable objectes */ if ( IS_BUFFER_OBJ( infop->mask) ) { if ( IS_FILE_OBJ( infop->mask) ) { newArgp[Idx].argClass = DtACTION_BUFFER; newArgp[Idx].u.buffer.type = XtNewString(infop->type); /* * RWV: * If did the following, the returned buffer name * may NOT be the same as the name which was passed * in to us (e.g. we were unable to create a tmp file * with the desired name). At present there is nothing * that preseves the original buffer name. * * newArgp[Idx].u.buffer.name = _DtBasename(infop->name); * * instead we'll set the returned name field to NULL; this * is after all a new copy of the buffer. */ newArgp[Idx].u.buffer.name = NULL; newArgp[Idx].u.buffer.writable = True; newArgp[Idx].u.buffer.bp = _DtActReadTmpFileToBuffer( infop->name, &newArgp[Idx].u.buffer.size ); } else { /* * We already have the buffer in memory * -- THIS SHOULD NEVER BE THE CASE FOR COMMAND ACTIONS-- */ myassert( 0 /* should never get here */ ); } } else if ( IS_FILE_OBJ( infop->mask) ) { /* * RWV * What about file objects passed in with the String qualifier? * This returns the original string string which may or may not * have been a file name. Are we OK here? */ newArgp[Idx].argClass = DtACTION_FILE; newArgp[Idx].u.file.name = XtNewString( infop->name ); } else myassert( 0 /* unsupported object */ ); } return invp->ac; } /* * Check the command invoker's queued commands for an instance of * the given invocation id. */ int _DtCmdCheckQForId(DtActionInvocationID id) { extern Cmd_RequestQueue * _DtCmdGetQueueHead( void ); Cmd_RequestQueue *pNode; CallbackData *dp; for ( pNode = _DtCmdGetQueueHead(); pNode; pNode = pNode->next ) { dp = (CallbackData *)(pNode->success_data); if ( dp->requestPtr->invocId == id ) return 1; /* found a match */ } /* Unable to find matching invocation id in the queue */ return 0; }