/* * 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 libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* *+SNOTICE * * * $TOG: IO.C /main/33 1999/01/29 14:45:00 mgreess $ * * RESTRICTED CONFIDENTIAL INFORMATION: * * The information in this document is subject to special * restrictions in a confidential disclosure agreement bertween * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this * document outside HP, IBM, Sun, USL, SCO, or Univel wihtout * Sun's specific written approval. This documment and all copies * and derivative works thereof must be returned or destroyed at * Sun's request. * * Copyright 1993 Sun Microsystems, Inc. All rights reserved. * *+ENOTICE */ #define X_INCLUDE_TIME_H #define X_INCLUDE_GRP_H #define X_INCLUDE_PWD_H #define XOS_USE_NO_LOCKING #include #include #include #include #include #include #include #include // // START Order dependent for AIX // #include #if defined(_aix) #include #if defined(BIG_ENDIAN) #undef BIG_ENDIAN #endif #endif #include #include #include // // END Order dependent for AIX // #include #if !defined(__linux__) # include #endif #include #include #include #include #ifdef _AIX #include #include #include #define IOVMAX MSG_MAXIOVLEN extern "C" { fchmod(int, mode_t); statfs(const char *, struct statfs *); mntctl(int, int, char *); ssize_t writev(int, const struct iovec *, int); } #if (OSMAJORVERSION==4) && (OSMINORVERSION==2) /* Temporary hack till the /usr/lpp/xlC/include/unistd.h file is fixed. */ extern "C" { int lockf(int, int, off_t); } #endif #endif /* _AIX */ #include #include #include #include #if !defined(IOV_MAX) #if !defined(__linux__) #include #endif #if !defined(DEF_IOV_MAX) #define DEF_IOV_MAX 16 #endif #define IOV_MAX DEF_IOV_MAX #endif #define IOVEC_IOVBASE_INCREMENTOR(iov, bytes) \ ((caddr_t)((size_t)iov->iov_base+bytes)) // The following I/O routines are wrappers for the normal routines, // but they deal with EINTR, and partial read/write situations. // // int SafeOpen(const char * path, int oflag, mode_t mode) { int status; do { status = open(path, oflag, mode); } while (status < 0 && errno == EINTR); return(status); } int SafeClose(int fd) { int status; do { status = close(fd); } while (status < 0 && errno == EINTR); return(status); } int SafeLink(const char * existingPath, const char * newPath) { int status; do { status = link(existingPath, newPath); } while (status < 0 && errno == EINTR); return(status); } int SafeRename(const char * oldPath, const char * newPath) { int status; do { status = rename(oldPath, newPath); } while (status < 0 && errno == EINTR); return(status); } int SafeRemove(const char *path) { int status; do { status = remove(path); } while (status < 0 && errno == EINTR); return(status); } int SafeUnlink(const char *path) { int status; do { status = unlink(path); } while (status < 0 && errno == EINTR); return(status); } ssize_t SafeRead(int fd, void * buf, size_t bytes) { ssize_t status = 0; do { status = read(fd, buf, bytes); } while(status < 0 && errno == EINTR); return(status); } ssize_t SafeWrite(int fd, const void * buf, size_t bytes) { ssize_t status = 0; do { status = write(fd, buf, bytes); } while(status < 0 && errno == EINTR); return(status); } // SafeWritev -- safe multiply vectored write // Description: // SafeWritev provides an interrupt safe way to call the writev() system // call. In addition, it removes the IOV_MAX limitation on the size of the // iovec structure, and it will not return until either all bytes specified // by the iovec structure are written or writev() returns an error. // Arguments: // int fd -- file descriptor to write to // struct iovec *iov -- iovec structure describing writes to be done // int iovcnt -- number of elements in passed in iov structure // Outputs: // Will effectively destroy the contents of the passed in iovec structure. // The caller must deallocate the storage associated with the structure // upon regaining control. // Returns: // == -1 : error returned from writev() - errno set to specific error number // != -1 : number of bytes actually written to the file unsigned long SafeWritev(int fd, struct iovec *iov, int iovcnt) { ssize_t status; unsigned long bytesWritten = 0; // outer loop: starting with the first write vector, as long as there is // at least one vector left to feed writev(), do so // for(int curIov = 0; curIov < iovcnt; ) { // inner loop: feed writev() allowing for interrupted system call do { status = writev( fd, &iov[curIov], (iovcnt-curIov) > IOV_MAX ? IOV_MAX : (iovcnt-curIov)); } while (status < 0 && errno == EINTR); if (status == (ssize_t)-1) // true error from writev?? return((unsigned long)-1); // yes: bail at this point to caller bytesWritten += status; // bump # bytes written count // must now "walk through" the io vector until we are up the to point // indicated by the number of bytes written by writev() - any leftover // in status indicates a partial write of a vector // while ((status > 0) && (curIov < iovcnt) && (iov[curIov].iov_len <= status)) { status -= iov[curIov++].iov_len; } // Check to see if we have reached the end of the io vector array; also // check to see if more bytes were written than called for; as crazy as // this sounds, in at least one instance when we finish spinning through // the io vectors we still had bytes left that had been written but not // accounted for in the io vectors (status > 0 && curIov >= iovcnt) // if (curIov >= iovcnt) { // out of IO vectors? if (status > 0) { // yes: all data accounted for? DtMailEnv error; // NO:: log error condition error.logError( DTM_TRUE, "SafeWritev: writev(): too many bytes written (%d/%d)\n", status, bytesWritten); } continue; // continue and let for loop exit } // Check for a partial write: if the current vector contains more data // than what has been written, writev() bailed in the middle of writing // a vector - adjust the vector and and feed it back to writev() again // OTHERWISE writev() ended with the current vector so move on to the next // if (iov[curIov].iov_len == status) // full write of this vector? curIov++; // yes: move on to the next vector else if (status != 0) { // no: adjust this vector and retry iov[curIov].iov_len -= status; iov[curIov].iov_base = IOVEC_IOVBASE_INCREMENTOR((&iov[curIov]), status); } } return(bytesWritten); } #define SWS_BUFFERSIZE 1024 ssize_t SafeWriteStrip(int fd, const void * buf, size_t bytes) { ssize_t status = 0; int i, j; char *ptr = (char*)buf, *writebuf; // bug 5856: don't write out control M // make a finite size buffer for writing writebuf = (char*) malloc(bytes < SWS_BUFFERSIZE ? bytes : SWS_BUFFERSIZE); for (i = 0, j = 0; i < bytes; i++, ptr++) { if (*ptr == '\r' && *(ptr+1) == '\n') continue; writebuf[j++] = *ptr; if (j == SWS_BUFFERSIZE || i == (bytes-1)) { do { status = write(fd, writebuf, j); } while(status < 0 && errno == EINTR); j = 0; } } free(writebuf); return(status); } int SafeStat(const char * path, struct stat * buf) { int status; do { status = stat(path, buf); } while (status < 0 && errno == EINTR); return(status); } int SafeFchown(int fd, uid_t owner, gid_t group) { int status; do { status = fchown(fd, owner, group); } while (status < 0 && errno == EINTR); return(status); } int SafeLStat(const char * path, struct stat * buf) { int status; do { status = lstat(path, buf); } while (status < 0 && errno == EINTR); return(status); } int SafeFStat(int fd, struct stat * buf) { int status; do { status = fstat(fd, buf); } while (status < 0 && errno == EINTR); return(status); } // SafeGuaranteedStat - return stat info for object given path name // If NFS attribute caching is enabled (which is the default), a // stat/fstat of a NFS file may not return the correct true size of the // mailbox if it has been appended to since the last time it was read. // To get around this problem, this function will perform a open(), // read() of 1 byte, fstat(), close() which will force the attributes // for the named file to be retrieved directly from the server. // int SafeGuaranteedStat(const char * path, struct stat * buf) { int saveErrno; #ifndef O_RSYNC int tempFd = SafeOpen(path, O_RDONLY|O_SYNC); #else int tempFd = SafeOpen(path, O_RDONLY|O_RSYNC|O_SYNC); #endif /* O_RSYNC */ if (tempFd == -1) { return(-1); } char tempBuf; if (SafeRead(tempFd, &tempBuf, 1) == -1) { saveErrno = errno; (void) SafeClose(tempFd); errno = saveErrno; return(-1); } if (SafeFStat(tempFd, buf) == -1) { saveErrno = errno; (void) SafeClose(tempFd); errno = saveErrno; return(-1); } (void) SafeClose(tempFd); return(0); } int SafeTruncate(const char * path, off_t len) { int status; do { status = truncate((char *)path, len); // The cast is for AIX } while (status < 0 && errno == EINTR); return(status); } int SafeFTruncate(int fd, off_t len) { int status; do { status = ftruncate(fd, len); } while (status < 0 && errno == EINTR); return(status); } int SafeAccess(const char * path, int amode) { int status; do { status = access(path, amode); } while (status < 0 && errno == EINTR); return(status); } #if defined(sun) #define LOCKF_SIZE_TYPE long #else #define LOCKF_SIZE_TYPE off_t #endif int SafeLockf(int fd, int func, long size) { int status; do { status = lockf(fd, func, (LOCKF_SIZE_TYPE) size); } while (status < 0 && errno == EINTR); return(status); } int SafeFChmod(int fd, mode_t mode) { int status; do { status = fchmod(fd, mode); } while (status < 0 && errno == EINTR); return(status); } int SafeDup2(int fd1, int fd2) { int status; do { status = dup2(fd1, fd2); } while (status < 0 && errno == EINTR); return(status); } int SafeExecvp(const char * file, char *const *argv) { int status; do { status = execvp(file, (char * const *)argv); } while (status < 0 && errno == EINTR); return(status); } int SafeWaitpid(pid_t proc, int * p_stat, int options) { pid_t status; do { status = waitpid(proc, p_stat, options); } while (status < 0 && errno == EINTR); return((int)status); } int SafeWait(int * p_stat) { pid_t status; do { status = wait(p_stat); } while (status < 0 && errno == EINTR); return((int)status); } int SafeUTime(const char * path, utimbuf * ntime) { int status; do { status = utime(path, ntime); } while (status < 0 && errno == EINTR); return(status); } void SafePathIsAccessible(DtMailEnv &error, const char *path) { int status; status = SafeAccess(path, F_OK); if (-1 == status) { if (EACCES == errno) error.vSetError(DTME_PathElementPermissions, DTM_FALSE, NULL, path); else if (ENOTDIR == errno) error.vSetError(DTME_PathElementNotDirectory, DTM_FALSE, NULL, path); else if (ENOENT == errno) { char *s, *t; t = strdup(path); s = strrchr(t, '/'); if (s && s != t) { *s = '\0'; status = SafeAccess(t, F_OK); if (-1 == status) error.vSetError( DTME_PathElementDoesNotExist, DTM_FALSE, NULL, path); } } } } #if defined(POSIX_THREADS) static void * time_mutex = NULL; #endif void SafeCtime(const time_t *clock, char * buf, int buflen) { _Xctimeparams ctime_buf; char *result; memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams)); result = _XCtime(clock, ctime_buf); if (result) strncpy(buf, result, buflen); else *buf = '\0'; } void SafeLocaltime(const time_t *clock, tm & result) { struct tm *time_ptr; _Xltimeparams localtime_buf; memset((void*) &localtime_buf, 0, sizeof(_Xltimeparams)); time_ptr = _XLocaltime(clock, localtime_buf); result = *time_ptr; } time_t SafeMktime(tm * timeptr) { #if defined(POSIX_THREADS) if (!time_mutex) { time_mutex = MutexInit(); } MutexLock lock_scope(time_mutex); #endif return(mktime(timeptr)); } size_t SafeStrftime(char * buf, size_t buf_size, const char * format, const tm * timeptr) { #if defined(POSIX_THREADS) if (!time_mutex) { time_mutex = MutexInit(); } MutexLock lock_scope(time_mutex); #endif return(strftime(buf, buf_size, format, timeptr)); } #define SockINTERNAL_BUFSIZE 2048 void *SockOpen(char *host, int clientPort, char **errorstring) { int sockfd; unsigned long inaddr; struct sockaddr_in ad; struct hostent *hp; DtMailEnv error; char *errorfmt = NULL; memset(&ad, 0, sizeof(ad)); ad.sin_family = AF_INET; inaddr = inet_addr(host); if (inaddr != -1) memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); else { hp = gethostbyname(host); if (hp == NULL) { if (NULL != errorstring) { errorfmt = DtMailEnv::getMessageText( SockErrorSet, 2, "Unknown host: %s"); if (NULL == *errorstring) *errorstring = (char*) malloc(BUFSIZ); sprintf(*errorstring, errorfmt, host); } return (FILE*) NULL; } memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); } ad.sin_port = htons(clientPort); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { if (NULL != errorstring) { errorfmt = DtMailEnv::getMessageText( SockErrorSet, 3, "Error creating socket: %s"); if (NULL == *errorstring) *errorstring = (char*) malloc(BUFSIZ); sprintf(*errorstring, errorfmt, error.errnoMessage()); } return (FILE*) NULL; } if (connect(sockfd, (struct sockaddr *) &ad, sizeof(ad)) < 0) { if (NULL != errorstring) { errorfmt = DtMailEnv::getMessageText( SockErrorSet, 3, "Error connecting to socket: %s"); if (NULL == *errorstring) *errorstring = (char*) malloc(BUFSIZ); sprintf(*errorstring, errorfmt, error.errnoMessage()); } close(sockfd); return (FILE*) NULL; } #if defined(USE_SOCKSTREAM) FILE *sockfp = fdopen(sockfd, "r+"); setvbuf(sockfp, NULL, _IONBF, SockINTERNAL_BUFSIZE); return (void*) sockfp; #else return (void*) sockfd; #endif } int SockPrintf(void *sockfp, char* format, ...) { va_list ap; char *buf = new char[8192]; int i; va_start(ap, format); vsprintf(buf, format, ap); va_end(ap); i = SockWrite(buf, 1, strlen(buf), sockfp); delete [] buf; return i; } char *SockGets(char *buf, int len, void *sockfp) { #if defined(USE_SOCKSTREAM) char *in = fgets(buf, len, (FILE*) sockfp); fseek((FILE*) sockfp, 0L, SEEK_CUR); /* required by POSIX */ return in; #else size_t n; char *bufp; n = 0; bufp = buf-1; do { bufp++; read((int) sockfp, (void*) bufp, 1); n++; } while (*bufp != '\n'); *(bufp+1) = '\0'; return buf; #endif } int SockRead(char *buf, int size, int len, void *sockfp) { #if defined(USE_SOCKSTREAM) int n = fread(buf, size, len, (FILE*) sockfp); fseek((FILE*) sockfp, 0L, SEEK_CUR); /* required by POSIX */ #else int n = (int) read((int) sockfp, (void*) buf, (size_t) size * len); #endif return n; } int SockWrite(char *buf, int size, int len, void *sockfp) { int n; #if defined(USE_SOCKSTREAM) n = fwrite(buf, size, len, (FILE*) sockfp); fseek((FILE*) sockfp, 0L, SEEK_CUR); /* required by POSIX */ #else n = write((int) sockfp, buf, size * len); #endif return n; } void SockClose(void *sockfp) { #if defined(USE_SOCKSTREAM) fclose((FILE*) sockfp); #else close((int) sockfp); #endif } void GetGroupName(char * grp_name) { struct group *grp; _Xgetgrparams grp_buf; memset((void*) &grp_buf, 0, sizeof(_Xgetgrparams)); grp = _XGetgrgid(getegid(), grp_buf); if (grp) { strcpy(grp_name, grp->gr_name); } else { strcpy(grp_name, "UNKNOWN_GROUP"); } } gid_t GetIdForGroupName(char * grp_name) { assert(grp_name != NULL); struct group *grp; _Xgetgrparams grp_buf; memset((void*) &grp_buf, 0, sizeof(_Xgetgrparams)); grp = _XGetgrnam(grp_name, grp_buf); return(grp ? grp->gr_gid : (gid_t)-1); } void GetPasswordEntry(passwd & result) { static struct passwd passwordEntry; static int oneTimeFlag = 0; if (!oneTimeFlag) { _Xgetpwparams pw_buf; struct passwd *tresult; memset((void*) &pw_buf, 0, sizeof(_Xgetpwparams)); #if defined(_AIX) _Xos_processLock; tresult = getpwuid(getuid()); _Xos_processUnlock; #else tresult = _XGetpwuid(getuid(), pw_buf); #endif assert(tresult != NULL); memcpy(&passwordEntry, tresult, sizeof(struct passwd)); passwordEntry.pw_name = strdup(passwordEntry.pw_name); passwordEntry.pw_passwd = strdup(passwordEntry.pw_passwd); #if !defined(_AIX) && !defined(__linux__) && !defined(CSRG_BASED) passwordEntry.pw_age = strdup(passwordEntry.pw_age); passwordEntry.pw_comment = strdup(passwordEntry.pw_comment); #endif passwordEntry.pw_gecos = strdup(passwordEntry.pw_gecos); passwordEntry.pw_dir = strdup(passwordEntry.pw_dir); passwordEntry.pw_shell = strdup(passwordEntry.pw_shell); oneTimeFlag++; } memcpy(&result, &passwordEntry, sizeof(struct passwd)); return; } #if defined(MAILGROUP_REQUIRED) int isSetMailGidNeeded(const char * mailboxPath) { assert(mailboxPath); char inbox_path[1024]; char mbox_path[1024]; struct passwd *pw; struct stat buf; struct stat buf1; _Xgetpwparams pw_buf; memset((void*) &pw_buf, 0, sizeof(_Xgetpwparams)); pw = _XGetpwuid(getuid(), pw_buf); // construct the lockfile name for the user. char *lock_file = (char *)calloc(1,strlen(pw->pw_name) + 6); strcpy(lock_file,pw->pw_name); strcat(lock_file,".lock"); // parse the mailfolder name from the path char *p = strrchr(mailboxPath,'/'); int len = (NULL!=0) ? strlen(mailboxPath)-strlen(p) : strlen(mailboxPath); char *str = (char *) calloc(1, len+1); strncpy(str, mailboxPath, len); str[len] = '\0'; stat(str,&buf); // Default or system mailbox dir name. strcpy(inbox_path, "/var/spool/mail"); stat(inbox_path,&buf1); free(str); if( ( (buf.st_dev == buf1.st_dev) && (buf.st_ino == buf1.st_ino) ) ) { if( !strcmp((p+1),pw->pw_name) || !strcmp((p+1),lock_file) ) { if( access(mailboxPath,R_OK) == -1 ) { free(lock_file); return (1); } } free(lock_file); return ( 0 ); } else { free(lock_file); return ( 1 ); } } #endif #ifdef _AIX /* * NAME: get_stat * * FUNCTION: * gather mount status for all virtual mounts for this host. * * EXECUTION ENVIRONMENT: Part of user command. * * ON ENTRY: * vmountpp pointer to a buffer in which to put mount info * * NOTES: * get_stat() was lifted and slightly modified from * AIX Version 3 df.c. * * RETURNS: * < 0 error in mntctl() * > 0 for number of struct vmounts (mount points) in * buffer which is pointed to by pointer left at * *vmountpp. */ int get_stat( struct vmount **vmountpp) /* place to tell where buffer is */ { size_t size; struct vmount *vm; int nmounts; int count; size = BUFSIZ; /* initial default size */ count = 10; /* don't try forever */ while (count--) { /* don't try it forever */ if ((vm = (struct vmount*) malloc(size)) == NULL) { return(-1); } /* * perform the QUERY mntctl - if it returns > 0, that is the * number of vmount structures in the buffer. If it returns * -1, an error occurred. If it returned 0, then look in * first word of buffer for needed size. */ if ((nmounts = mntctl(MCTL_QUERY, size, (caddr_t)vm)) > 0) { *vmountpp = vm; /* OK, got it, now return */ return(nmounts); } else { if (nmounts == 0) { size = *(int *)vm; /* the buffer wasn't big enough */ free((void *)vm); /* get required buffer size */ } else { free((void *)vm);/* some other kind of error occurred*/ return(-1); } } } return(-1); } /* * NAME: get_vmount * * FUNCTION: * Determines, via the filesystems vmount structures, * the vmount id of the the filesystem id provided as * an argument (enables ultimate access to the actual * name of the filesystem). * * EXECUTION ENVIRONMENT: Part of user command. * * RETURNS: * ptr to structure with vmount id of filesystem or NULL */ struct vmount *get_vmount(fsid_t *fsid) { struct vmount *inu_vmount_p=NULL; int inu_vmount_num; struct vmount *vm; int nmount; /* make sure we have all the virtual mount status of this host */ if(inu_vmount_p == NULL) inu_vmount_num = get_stat(&inu_vmount_p); /* get the number of struct vmount in the vmount buffer */ nmount = inu_vmount_num; /* go thru all the structures in the vmount buffer */ for (vm = inu_vmount_p; nmount; nmount--, vm = (struct vmount *)((char *)vm + vm->vmt_length)) { if(( vm->vmt_fsid.fsid_dev == fsid->fsid_dev ) && ( vm->vmt_fsid.fsid_type == fsid->fsid_type )) return(vm); } return((struct vmount *)NULL); } #endif /* _AIX */ int FileSystemSpace(const char *file_path, size_t bytes, char **fsname) { int fserror=FALSE; struct stat stat_buf; size_t req_space = 0; #if !defined(__linux__) struct statvfs statvfs_buf; #endif if (stat(file_path,&stat_buf) < 0) return 0; #if !defined(__linux__) if (statvfs(file_path,&statvfs_buf) < 0) return 0; #endif #ifdef _AIX struct statfs statfs_buf; if (statfs(file_path,&statfs_buf) < 0) return 0; if (statfs_buf.f_vfstype == MNT_NFS) { struct vmount *vm; vm = get_vmount(&(statfs_buf.f_fsid)); strcpy (statfs_buf.f_fname, vmt2dataptr (vm, VMT_STUB)); } #endif /* _AIX */ if (bytes != 0) { // The following code became redundant as the writeMailBox method now // checks for No space from the SafeWritev call. #if 0 // CDExc20314 // This check does not work (HP, IBM, Sun return -1; DEC returns 0) // Since the calling code has already created the file and is only // check that there is enough space, we don't need to check that // there are enough inodes. // // special case where the filesystem is out of inodes. if(statvfs_buf.f_ffree < 10) fserror = TRUE; #endif if (! fserror) { req_space = (size_t) ((bytes > stat_buf.st_size) ? (bytes-stat_buf.st_size) : 0); #if !defined(__linux__) if ( (statvfs_buf.f_bfree*statvfs_buf.f_bsize) > (req_space + statvfs_buf.f_bsize) ) return 1; else fserror = TRUE; #endif } } else fserror = TRUE; if (fserror) { #ifdef _AIX *fsname = (char*) calloc(1, strlen(statfs_buf.f_fname)+1); strcpy (*fsname, statfs_buf.f_fname); #else *fsname = (char*) calloc(1, strlen(file_path)+1); strcpy (*fsname, file_path); #endif return 0; } return 1; }