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
27 * $TOG: IO.C /main/33 1999/01/29 14:45:00 mgreess $
29 * RESTRICTED CONFIDENTIAL INFORMATION:
31 * The information in this document is subject to special
32 * restrictions in a confidential disclosure agreement bertween
33 * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
34 * document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
35 * Sun's specific written approval. This documment and all copies
36 * and derivative works thereof must be returned or destroyed at
39 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
44 #define X_INCLUDE_TIME_H
45 #define X_INCLUDE_GRP_H
46 #define X_INCLUDE_PWD_H
47 #define XOS_USE_NO_LOCKING
48 #include <X11/Xos_r.h>
58 // START Order dependent for AIX
60 #include <sys/socket.h>
62 #include <sys/socketvar.h>
63 #if defined(BIG_ENDIAN)
67 #include <netinet/in.h>
69 #include <arpa/inet.h>
71 // END Order dependent for AIX
74 #if !defined(__linux__)
75 # include <sys/statvfs.h>
77 #include <sys/types.h>
83 #include <sys/mntctl.h>
84 #include <sys/statfs.h>
86 #define IOVMAX MSG_MAXIOVLEN
90 statfs(const char *, struct statfs *);
91 mntctl(int, int, char *);
92 ssize_t writev(int, const struct iovec *, int);
94 #if (OSMAJORVERSION==4) && (OSMINORVERSION==2)
95 /* Temporary hack till the /usr/lpp/xlC/include/unistd.h file is fixed. */
96 extern "C" { int lockf(int, int, off_t); }
100 #include <DtMail/DtMail.hh>
101 #include <DtMail/DtMailError.hh>
102 #include <DtMail/IO.hh>
103 #include <DtMail/Threads.hh>
105 #if !defined(IOV_MAX)
106 #if !defined(__linux__)
107 #include <sys/stream.h>
109 #if !defined(DEF_IOV_MAX)
110 #define DEF_IOV_MAX 16
112 #define IOV_MAX DEF_IOV_MAX
115 #define IOVEC_IOVBASE_INCREMENTOR(iov, bytes) \
116 ((caddr_t)((size_t)iov->iov_base+bytes))
118 // The following I/O routines are wrappers for the normal routines,
119 // but they deal with EINTR, and partial read/write situations.
123 SafeOpen(const char * path, int oflag, mode_t mode)
128 status = open(path, oflag, mode);
129 } while (status < 0 && errno == EINTR);
140 } while (status < 0 && errno == EINTR);
146 SafeLink(const char * existingPath, const char * newPath)
150 status = link(existingPath, newPath);
151 } while (status < 0 && errno == EINTR);
157 SafeRename(const char * oldPath, const char * newPath)
161 status = rename(oldPath, newPath);
162 } while (status < 0 && errno == EINTR);
168 SafeRemove(const char *path)
172 status = remove(path);
173 } while (status < 0 && errno == EINTR);
179 SafeUnlink(const char *path)
183 status = unlink(path);
184 } while (status < 0 && errno == EINTR);
190 SafeRead(int fd, void * buf, size_t bytes)
194 status = read(fd, buf, bytes);
195 } while(status < 0 && errno == EINTR);
201 SafeWrite(int fd, const void * buf, size_t bytes)
205 status = write(fd, buf, bytes);
206 } while(status < 0 && errno == EINTR);
211 // SafeWritev -- safe multiply vectored write
213 // SafeWritev provides an interrupt safe way to call the writev() system
214 // call. In addition, it removes the IOV_MAX limitation on the size of the
215 // iovec structure, and it will not return until either all bytes specified
216 // by the iovec structure are written or writev() returns an error.
218 // int fd -- file descriptor to write to
219 // struct iovec *iov -- iovec structure describing writes to be done
220 // int iovcnt -- number of elements in passed in iov structure
222 // Will effectively destroy the contents of the passed in iovec structure.
223 // The caller must deallocate the storage associated with the structure
224 // upon regaining control.
226 // == -1 : error returned from writev() - errno set to specific error number
227 // != -1 : number of bytes actually written to the file
230 SafeWritev(int fd, struct iovec *iov, int iovcnt)
233 unsigned long bytesWritten = 0;
235 // outer loop: starting with the first write vector, as long as there is
236 // at least one vector left to feed writev(), do so
238 for(int curIov = 0; curIov < iovcnt; ) {
240 // inner loop: feed writev() allowing for interrupted system call
245 (iovcnt-curIov) > IOV_MAX ? IOV_MAX : (iovcnt-curIov));
246 } while (status < 0 && errno == EINTR);
248 if (status == (ssize_t)-1) // true error from writev??
249 return((unsigned long)-1); // yes: bail at this point to caller
250 bytesWritten += status; // bump # bytes written count
252 // must now "walk through" the io vector until we are up the to point
253 // indicated by the number of bytes written by writev() - any leftover
254 // in status indicates a partial write of a vector
256 while ((status > 0) && (curIov < iovcnt) && (iov[curIov].iov_len <= status)) {
257 status -= iov[curIov++].iov_len;
260 // Check to see if we have reached the end of the io vector array; also
261 // check to see if more bytes were written than called for; as crazy as
262 // this sounds, in at least one instance when we finish spinning through
263 // the io vectors we still had bytes left that had been written but not
264 // accounted for in the io vectors (status > 0 && curIov >= iovcnt)
266 if (curIov >= iovcnt) { // out of IO vectors?
267 if (status > 0) { // yes: all data accounted for?
268 DtMailEnv error; // NO:: log error condition
271 "SafeWritev: writev(): too many bytes written (%d/%d)\n",
272 status, bytesWritten);
274 continue; // continue and let for loop exit
277 // Check for a partial write: if the current vector contains more data
278 // than what has been written, writev() bailed in the middle of writing
279 // a vector - adjust the vector and and feed it back to writev() again
280 // OTHERWISE writev() ended with the current vector so move on to the next
282 if (iov[curIov].iov_len == status) // full write of this vector?
283 curIov++; // yes: move on to the next vector
284 else if (status != 0) { // no: adjust this vector and retry
285 iov[curIov].iov_len -= status;
286 iov[curIov].iov_base = IOVEC_IOVBASE_INCREMENTOR((&iov[curIov]), status);
289 return(bytesWritten);
292 #define SWS_BUFFERSIZE 1024
294 SafeWriteStrip(int fd, const void * buf, size_t bytes)
298 char *ptr = (char*)buf, *writebuf;
300 // bug 5856: don't write out control M
301 // make a finite size buffer for writing
302 writebuf = (char*) malloc(bytes < SWS_BUFFERSIZE ? bytes : SWS_BUFFERSIZE);
304 for (i = 0, j = 0; i < bytes; i++, ptr++) {
305 if (*ptr == '\r' && *(ptr+1) == '\n')
307 writebuf[j++] = *ptr;
308 if (j == SWS_BUFFERSIZE || i == (bytes-1)) {
310 status = write(fd, writebuf, j);
311 } while(status < 0 && errno == EINTR);
321 SafeStat(const char * path, struct stat * buf)
325 status = stat(path, buf);
326 } while (status < 0 && errno == EINTR);
332 SafeFchown(int fd, uid_t owner, gid_t group)
336 status = fchown(fd, owner, group);
337 } while (status < 0 && errno == EINTR);
343 SafeLStat(const char * path, struct stat * buf)
347 status = lstat(path, buf);
348 } while (status < 0 && errno == EINTR);
354 SafeFStat(int fd, struct stat * buf)
358 status = fstat(fd, buf);
359 } while (status < 0 && errno == EINTR);
364 // SafeGuaranteedStat - return stat info for object given path name
365 // If NFS attribute caching is enabled (which is the default), a
366 // stat/fstat of a NFS file may not return the correct true size of the
367 // mailbox if it has been appended to since the last time it was read.
368 // To get around this problem, this function will perform a open(),
369 // read() of 1 byte, fstat(), close() which will force the attributes
370 // for the named file to be retrieved directly from the server.
373 SafeGuaranteedStat(const char * path, struct stat * buf)
378 int tempFd = SafeOpen(path, O_RDONLY|O_SYNC);
380 int tempFd = SafeOpen(path, O_RDONLY|O_RSYNC|O_SYNC);
388 if (SafeRead(tempFd, &tempBuf, 1) == -1) {
390 (void) SafeClose(tempFd);
395 if (SafeFStat(tempFd, buf) == -1) {
397 (void) SafeClose(tempFd);
402 (void) SafeClose(tempFd);
408 SafeTruncate(const char * path, off_t len)
412 status = truncate((char *)path, len); // The cast is for AIX
413 } while (status < 0 && errno == EINTR);
419 SafeFTruncate(int fd, off_t len)
423 status = ftruncate(fd, len);
424 } while (status < 0 && errno == EINTR);
430 SafeAccess(const char * path, int amode)
434 status = access(path, amode);
435 } while (status < 0 && errno == EINTR);
441 #define LOCKF_SIZE_TYPE long
443 #define LOCKF_SIZE_TYPE off_t
447 SafeLockf(int fd, int func, long size)
451 status = lockf(fd, func, (LOCKF_SIZE_TYPE) size);
452 } while (status < 0 && errno == EINTR);
458 SafeFChmod(int fd, mode_t mode)
462 status = fchmod(fd, mode);
463 } while (status < 0 && errno == EINTR);
469 SafeDup2(int fd1, int fd2)
473 status = dup2(fd1, fd2);
474 } while (status < 0 && errno == EINTR);
480 SafeExecvp(const char * file, char *const *argv)
485 status = execvp(file, (char * const *)argv);
487 } while (status < 0 && errno == EINTR);
493 SafeWaitpid(pid_t proc, int * p_stat, int options)
497 status = waitpid(proc, p_stat, options);
498 } while (status < 0 && errno == EINTR);
504 SafeWait(int * p_stat)
508 status = wait(p_stat);
509 } while (status < 0 && errno == EINTR);
515 SafeUTime(const char * path, utimbuf * ntime)
519 status = utime(path, ntime);
520 } while (status < 0 && errno == EINTR);
526 SafePathIsAccessible(DtMailEnv &error, const char *path)
530 status = SafeAccess(path, F_OK);
534 error.vSetError(DTME_PathElementPermissions, DTM_FALSE, NULL, path);
535 else if (ENOTDIR == errno)
536 error.vSetError(DTME_PathElementNotDirectory, DTM_FALSE, NULL, path);
537 else if (ENOENT == errno)
546 status = SafeAccess(t, F_OK);
549 DTME_PathElementDoesNotExist,
557 #if defined(POSIX_THREADS)
558 static void * time_mutex = NULL;
562 SafeCtime(const time_t *clock, char * buf, int buflen)
564 _Xctimeparams ctime_buf;
567 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
568 result = _XCtime(clock, ctime_buf);
570 strncpy(buf, result, buflen);
576 SafeLocaltime(const time_t *clock, tm & result)
579 _Xltimeparams localtime_buf;
581 memset((void*) &localtime_buf, 0, sizeof(_Xltimeparams));
582 time_ptr = _XLocaltime(clock, localtime_buf);
587 SafeMktime(tm * timeptr)
589 #if defined(POSIX_THREADS)
592 time_mutex = MutexInit();
595 MutexLock lock_scope(time_mutex);
598 return(mktime(timeptr));
602 SafeStrftime(char * buf, size_t buf_size,
603 const char * format, const tm * timeptr)
605 #if defined(POSIX_THREADS)
608 time_mutex = MutexInit();
611 MutexLock lock_scope(time_mutex);
614 return(strftime(buf, buf_size, format, timeptr));
617 #define SockINTERNAL_BUFSIZE 2048
619 void *SockOpen(char *host, int clientPort, char **errorstring)
622 unsigned long inaddr;
623 struct sockaddr_in ad;
626 char *errorfmt = NULL;
628 memset(&ad, 0, sizeof(ad));
629 ad.sin_family = AF_INET;
631 inaddr = inet_addr(host);
633 memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
636 hp = gethostbyname(host);
639 if (NULL != errorstring)
641 errorfmt = DtMailEnv::getMessageText(
644 if (NULL == *errorstring) *errorstring = (char*) malloc(BUFSIZ);
645 sprintf(*errorstring, errorfmt, host);
649 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
651 ad.sin_port = htons(clientPort);
653 sockfd = socket(AF_INET, SOCK_STREAM, 0);
656 if (NULL != errorstring)
658 errorfmt = DtMailEnv::getMessageText(
660 "Error creating socket: %s");
661 if (NULL == *errorstring) *errorstring = (char*) malloc(BUFSIZ);
662 sprintf(*errorstring, errorfmt, error.errnoMessage());
666 if (connect(sockfd, (struct sockaddr *) &ad, sizeof(ad)) < 0)
668 if (NULL != errorstring)
670 errorfmt = DtMailEnv::getMessageText(
672 "Error connecting to socket: %s");
673 if (NULL == *errorstring) *errorstring = (char*) malloc(BUFSIZ);
674 sprintf(*errorstring, errorfmt, error.errnoMessage());
680 #if defined(USE_SOCKSTREAM)
681 FILE *sockfp = fdopen(sockfd, "r+");
682 setvbuf(sockfp, NULL, _IONBF, SockINTERNAL_BUFSIZE);
683 return (void*) sockfp;
685 return (void*) sockfd;
689 int SockPrintf(void *sockfp, char* format, ...)
692 char *buf = new char[8192];
695 va_start(ap, format);
696 vsprintf(buf, format, ap);
698 i = SockWrite(buf, 1, strlen(buf), sockfp);
703 char *SockGets(char *buf, int len, void *sockfp)
705 #if defined(USE_SOCKSTREAM)
706 char *in = fgets(buf, len, (FILE*) sockfp);
707 fseek((FILE*) sockfp, 0L, SEEK_CUR); /* required by POSIX */
718 read((int) sockfp, (void*) bufp, 1);
720 } while (*bufp != '\n');
726 int SockRead(char *buf, int size, int len, void *sockfp)
728 #if defined(USE_SOCKSTREAM)
729 int n = fread(buf, size, len, (FILE*) sockfp);
730 fseek((FILE*) sockfp, 0L, SEEK_CUR); /* required by POSIX */
732 int n = (int) read((int) sockfp, (void*) buf, (size_t) size * len);
737 int SockWrite(char *buf, int size, int len, void *sockfp)
741 #if defined(USE_SOCKSTREAM)
742 n = fwrite(buf, size, len, (FILE*) sockfp);
743 fseek((FILE*) sockfp, 0L, SEEK_CUR); /* required by POSIX */
745 n = write((int) sockfp, buf, size * len);
751 void SockClose(void *sockfp)
753 #if defined(USE_SOCKSTREAM)
754 fclose((FILE*) sockfp);
761 GetGroupName(char * grp_name)
764 _Xgetgrparams grp_buf;
766 memset((void*) &grp_buf, 0, sizeof(_Xgetgrparams));
767 grp = _XGetgrgid(getegid(), grp_buf);
770 strcpy(grp_name, grp->gr_name);
773 strcpy(grp_name, "UNKNOWN_GROUP");
779 GetIdForGroupName(char * grp_name)
781 assert(grp_name != NULL);
783 _Xgetgrparams grp_buf;
785 memset((void*) &grp_buf, 0, sizeof(_Xgetgrparams));
786 grp = _XGetgrnam(grp_name, grp_buf);
788 return(grp ? grp->gr_gid : (gid_t)-1);
792 GetPasswordEntry(passwd & result)
794 static struct passwd passwordEntry;
795 static int oneTimeFlag = 0;
798 _Xgetpwparams pw_buf;
799 struct passwd *tresult;
801 memset((void*) &pw_buf, 0, sizeof(_Xgetpwparams));
804 tresult = getpwuid(getuid());
807 tresult = _XGetpwuid(getuid(), pw_buf);
810 assert(tresult != NULL);
811 memcpy(&passwordEntry, tresult, sizeof(struct passwd));
812 passwordEntry.pw_name = strdup(passwordEntry.pw_name);
813 passwordEntry.pw_passwd = strdup(passwordEntry.pw_passwd);
814 #if !defined(_AIX) && !defined(__linux__) && !defined(CSRG_BASED)
815 passwordEntry.pw_age = strdup(passwordEntry.pw_age);
816 passwordEntry.pw_comment = strdup(passwordEntry.pw_comment);
818 passwordEntry.pw_gecos = strdup(passwordEntry.pw_gecos);
819 passwordEntry.pw_dir = strdup(passwordEntry.pw_dir);
820 passwordEntry.pw_shell = strdup(passwordEntry.pw_shell);
824 memcpy(&result, &passwordEntry, sizeof(struct passwd));
827 #if defined(MAILGROUP_REQUIRED)
828 int isSetMailGidNeeded(const char * mailboxPath)
833 char inbox_path[1024];
834 char mbox_path[1024];
838 _Xgetpwparams pw_buf;
840 memset((void*) &pw_buf, 0, sizeof(_Xgetpwparams));
841 pw = _XGetpwuid(getuid(), pw_buf);
843 // construct the lockfile name for the user.
844 char *lock_file = (char *)calloc(1,strlen(pw->pw_name) + 6);
845 strcpy(lock_file,pw->pw_name);
846 strcat(lock_file,".lock");
848 // parse the mailfolder name from the path
849 char *p = strrchr(mailboxPath,'/');
850 int len = (NULL!=0) ? strlen(mailboxPath)-strlen(p) : strlen(mailboxPath);
851 char *str = (char *) calloc(1, len+1);
852 strncpy(str, mailboxPath, len);
857 // Default or system mailbox dir name.
858 strcpy(inbox_path, "/var/spool/mail");
859 stat(inbox_path,&buf1);
862 if( ( (buf.st_dev == buf1.st_dev) && (buf.st_ino == buf1.st_ino) ) )
864 if( !strcmp((p+1),pw->pw_name) || !strcmp((p+1),lock_file) )
866 if( access(mailboxPath,R_OK) == -1 )
888 * gather mount status for all virtual mounts for this host.
890 * EXECUTION ENVIRONMENT: Part of user command.
893 * vmountpp pointer to a buffer in which to put mount info
896 * get_stat() was lifted and slightly modified from
897 * AIX Version 3 df.c.
900 * < 0 error in mntctl()
901 * > 0 for number of struct vmounts (mount points) in
902 * buffer which is pointed to by pointer left at
906 struct vmount **vmountpp) /* place to tell where buffer is */
909 register struct vmount *vm;
913 size = BUFSIZ; /* initial default size */
914 count = 10; /* don't try forever */
916 while (count--) { /* don't try it forever */
917 if ((vm = (struct vmount*) malloc(size)) == NULL) {
922 * perform the QUERY mntctl - if it returns > 0, that is the
923 * number of vmount structures in the buffer. If it returns
924 * -1, an error occurred. If it returned 0, then look in
925 * first word of buffer for needed size.
927 if ((nmounts = mntctl(MCTL_QUERY, size, (caddr_t)vm)) > 0) {
928 *vmountpp = vm; /* OK, got it, now return */
932 size = *(int *)vm; /* the buffer wasn't big enough */
933 free((void *)vm); /* get required buffer size */
935 free((void *)vm);/* some other kind of error occurred*/
947 * Determines, via the filesystems vmount structures,
948 * the vmount id of the the filesystem id provided as
949 * an argument (enables ultimate access to the actual
950 * name of the filesystem).
952 * EXECUTION ENVIRONMENT: Part of user command.
955 * ptr to structure with vmount id of filesystem or NULL
957 struct vmount *get_vmount(fsid_t *fsid)
959 struct vmount *inu_vmount_p=NULL;
961 register struct vmount *vm;
964 /* make sure we have all the virtual mount status of this host */
965 if(inu_vmount_p == NULL)
966 inu_vmount_num = get_stat(&inu_vmount_p);
968 /* get the number of struct vmount in the vmount buffer */
969 nmount = inu_vmount_num;
971 /* go thru all the structures in the vmount buffer */
972 for (vm = inu_vmount_p; nmount; nmount--,
973 vm = (struct vmount *)((char *)vm + vm->vmt_length)) {
974 if(( vm->vmt_fsid.fsid_dev == fsid->fsid_dev ) &&
975 ( vm->vmt_fsid.fsid_type == fsid->fsid_type ))
978 return((struct vmount *)NULL);
982 int FileSystemSpace(const char *file_path, size_t bytes, char **fsname)
985 struct stat stat_buf;
986 size_t req_space = 0;
987 #if !defined(__linux__)
988 struct statvfs statvfs_buf;
991 if (stat(file_path,&stat_buf) < 0) return 0;
992 #if !defined(__linux__)
993 if (statvfs(file_path,&statvfs_buf) < 0) return 0;
997 struct statfs statfs_buf;
999 if (statfs(file_path,&statfs_buf) < 0) return 0;
1000 if (statfs_buf.f_vfstype == MNT_NFS)
1003 vm = get_vmount(&(statfs_buf.f_fsid));
1004 strcpy (statfs_buf.f_fname, vmt2dataptr (vm, VMT_STUB));
1011 // The following code became redundant as the writeMailBox method now
1012 // checks for No space from the SafeWritev call.
1015 // This check does not work (HP, IBM, Sun return -1; DEC returns 0)
1016 // Since the calling code has already created the file and is only
1017 // check that there is enough space, we don't need to check that
1018 // there are enough inodes.
1020 // special case where the filesystem is out of inodes.
1021 if(statvfs_buf.f_ffree < 10) fserror = TRUE;
1026 req_space = (size_t) ((bytes > stat_buf.st_size) ?
1027 (bytes-stat_buf.st_size) :
1029 #if !defined(__linux__)
1030 if ( (statvfs_buf.f_bfree*statvfs_buf.f_bsize) >
1031 (req_space + statvfs_buf.f_bsize) )
1038 else fserror = TRUE;
1043 *fsname = (char*) calloc(1, strlen(statfs_buf.f_fname)+1);
1044 strcpy (*fsname, statfs_buf.f_fname);
1046 *fsname = (char*) calloc(1, strlen(file_path)+1);
1047 strcpy (*fsname, file_path);