This is sysdep.c in view mode; [Download] [Up]
/* @(#)src/sysdep.c 1.3 03 Dec 1990 00:33:02 */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * sysdep.c: * functions which may be operating system dependent. * * external functions: time_stamp, get_arpa_date, get_local_year, * unix_date, compute_sender, getfullname, * fopen_as_user, lock_file, unlock_file, * compute_hostname, open_child, close_child, * close_all, scan_dir, fcntl_rd_lock */ #include <stdio.h> #include <sys/types.h> #include <pwd.h> #include <ctype.h> #include <sys/stat.h> #include <errno.h> #include "defs.h" #include "smail.h" #include "child.h" #include "dys.h" #include "log.h" #include "exitcodes.h" #ifndef DEPEND # include "extern.h" # include "debug.h" #endif #if defined(UNIX_SYS5) || defined(INCLUDE_SYS_PARAM) # include <sys/param.h> #endif #if defined(UNIX_SYS5) || defined(POSIX_OS) || defined(USE_FCNTL) # include <fcntl.h> #endif #if defined(POSIX_OS) # include <limits.h> # include <unistd.h> #endif /* POSIX_OS */ #ifdef HAVE_UNAME # include <sys/utsname.h> #endif #if defined(UNIX_BSD) # include <sys/dir.h> #else # if defined(UNIX_XENIX) # include <sys/ndir.h> # else # if defined(HAVE_READDIR) # include <dirent.h> # define direct dirent # else # include <sys/dir.h> # endif /* defined(HAVE_READDIR) */ # endif /* not defined(UNIX_XENIX) */ #endif /* not defined(UNIX_BSD) */ #if defined(UNIX_BSD) || defined(USE_SYS_TIME) # include <sys/time.h> # include <sys/file.h> #else # include <time.h> #endif #if !defined(UNIX_BSD) && !defined(UNIX_SYS5) && !defined(POSIX_OS) /* use this for ancient ftime() system call, as a last resort */ # include <sys/timeb.h> #endif /* functions local to this file */ static char *get_time_zone(); static void fullname_from_gcos(); static void check_stale(); static int lock_file_by_name(); static void unlock_file_by_name(); /* imported library functions */ extern long lseek(); extern long time(); extern char *getenv(); extern char *getlogin(); extern struct tm *gmtime(); extern struct tm *localtime(); extern char *ctime(); #if !defined(UNIX_SYS5) && !defined(POSIX_OS) extern char *timezone(); #endif /* UNIX_SYS5 */ /* define these, if they aren't already */ #ifndef O_RDONLY # define O_RDONLY 0 #endif #ifndef O_WRONLY # define O_WRONLY 1 #endif #ifndef O_RDWR # define O_RDWR 2 #endif /* * time_stamp - return a time stamp string in the form: * * mm/dd/yy hh:mm:ss */ char * time_stamp() { #ifdef GLOTZNET long clock = time((long *)0) - tzoffset * 3600; #else /* GLOTZNET */ long clock = time((long *)0); #endif /* GLOTZNET */ struct tm *ltm = localtime(&clock); static char timebuf[sizeof("mm/dd/yy hh:mm:ss")]; (void) sprintf(timebuf, "%02d/%02d/%02d %02d:%02d:%02d", ltm->tm_mon+1, ltm->tm_mday, ltm->tm_year, ltm->tm_hour, ltm->tm_min, ltm->tm_sec); return timebuf; } /* * strings used by the date and time routines below */ static char *week_day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; /* * get_local_year - get the year according to local time */ int get_local_year() { long stamp; /* local time stamp in seconds form Epoch */ /* return year */ #ifdef GLOTZNET stamp = time((long *)0) - tzoffset * 3600; #else /* GLOTZNET */ stamp = time((long *)0); #endif /* GLOTZNET */ return (localtime(&stamp))->tm_year+1900; } /* * get_arpa_date - return date in RFC822 format */ char * get_arpa_date(clock) long clock; { #ifdef GLOTZNET struct tm *ltm; char *tz_name; static char timebuf[sizeof("ddd, dd mmm yy hh:mm tzn+hh:mm:ss")]; clock -= tzoffset * 3600; ltm = localtime(&clock); tz_name = get_time_zone(ltm); #else /* GLOTZNET */ struct tm *ltm = localtime(&clock); char *tz_name = get_time_zone(ltm); static char timebuf[sizeof("ddd, dd mmm yy hh:mm tzn+hh:mm:ss")]; #endif /* GLOTZNET */ if (tz_name == NULL) { tz_name = "GMT"; #ifdef GLOTZNET clock += tzoffset * 3600; #endif /* GLOTZNET */ ltm = gmtime(&clock); } (void) sprintf(timebuf, "%s, %d %s %02d %02d:%02d %s", week_day[ltm->tm_wday], ltm->tm_mday, months[ltm->tm_mon], ltm->tm_year, ltm->tm_hour, ltm->tm_min, tz_name); return timebuf; } /* * get_time_zone - get the current timezone */ /* ARGSUSED */ static char * get_time_zone(ltm) struct tm *ltm; { #if defined(UNIX_BSD) && !defined(POSIX_OS) || defined(USE_GETTIMEOFDAY) struct timeval tv; struct timezone tz; (void) gettimeofday(&tv, &tz); #ifdef GLOTZNET tz.tz_minuteswest += tzoffset * 60; if (tznodst) ltm->tm_isdst = 0; #endif /* GLOTZNET */ return timezone(tz.tz_minuteswest, ltm->tm_isdst); #else /* (not UNIX_BSD || POSIX_OS) && not USE_GETTIMEOFDAY */ # if defined(UNIX_SYS5) || defined(POSIX_OS) || defined(USE_TZNAME) extern char *tzname[2]; return tzname[ltm->tm_isdst]; # else /* not UNIX_SYS5 */ struct timeb timeb; /* try the V6/V7 system call for getting the time zone */ (void) ftime(&timeb); return timezone(timeb.timezone, ltm->tm_isdst); # endif /* not UNIX_SYS5 */ #endif /* not UNIX_BSD */ } /* * unix_date - get the current date as suitable for a From_ line * * Use ctime format. */ char * unix_date() { #ifdef GLOTZNET long clock = time((long *)0) - tzoffset * 3600; #else /* GLOTZNET */ long clock = time((long *)0); #endif /* GLOTZNET */ char *date = ctime(&clock); /* * XXX - ctime format is quite standard, so just but the nul in * the proper spot. */ date[24] = '\0'; /* get rid of that \n */ return date; } /* * compute_sender - compute the user running this program. * * We don't go through the passwd file cache, because the cache * doesn't keep the full name information. */ void compute_sender() { struct passwd *pw; struct passwd *getpwuid(); /* get a password file entry given uid */ islocal = TRUE; /* we can only compute local senders */ if (sender = getlogin()) { register char *new_sender = xmalloc(strlen(sender) + 1); (void) strcpy(new_sender, sender); sender = new_sender; return; } if (pw = getpwuid(real_uid)) { sender = xmalloc((unsigned)(strlen(pw->pw_name))); (void) strcpy(sender, pw->pw_name); if (!sender_name == NULL) { /* * might as well compute the full name while we have the * password entry on hand */ fullname_from_gcos(pw->pw_gecos); } return; } sender = "postmaster"; return; } /* * getfullname - get the full name of the sender * * The full name comes from the GCOS field in the password entry. */ void getfullname() { struct passwd *pw; struct passwd *getpwnam(); /* get a password entry given a username */ if (islocal) { /* only possible for local senders */ pw = getpwnam(sender); if (pw) { fullname_from_gcos(pw->pw_gecos); } } return; } /* * fullname_from_gcos - fill in sender_name from the given gcos field * * (Modified to strip 0000-name(0000) USG junk - hargen@pdn 8/20/88 */ static void fullname_from_gcos(gecos) register char *gecos; { struct str str; register struct str *sp = &str; /* put full name here */ char *cp; STR_INIT(sp); if (isdigit(*gecos) && (cp = index(gecos, '-')) != NULL) gecos = cp + 1; /* skip USG-style 0000-Name junk */ while (*gecos && *gecos != ',' && *gecos != '(') { /* name may end with a comma or USG-style (0000) junk */ if (*gecos == '&') { /* & means copy sender, capitalized */ STR_NEXT(sp, uppercase(sender[0])); STR_CAT(sp, sender+1); } else { STR_NEXT(sp, *gecos); } gecos++; } STR_NEXT(sp, '\0'); STR_DONE(sp); sender_name = sp->p; } /* * fopen_as_user - call fopen(3s) within the context of a specific uid/gid * * given a uid and gid, fopen a file only if the given uid/gid would be * able to do so. Also, for "a" and "w" types: if the file did not * previously exist, the file will be owned by the uid/gid after having * being opened (if the mailer is running as root). * * return value and errno are set as a call to fopen(3s) would set them. * * NOTE: the mode argument is only required if type is "a" or "w". */ /*VARARGS5*/ FILE * fopen_as_user(fn, type, check_path, uid, gid, mode) char *fn; /* name of file to open */ char *type; /* mode for fopen ("r", "w" or "a") */ int uid; /* user id */ int gid; /* group id */ int mode; /* mode for creat() */ { struct stat dir_stat; /* one for directories */ struct stat file_stat; /* one for the file */ char *fn_build; /* area for building test filenames */ register char *fnp; /* pointer for scanning fn */ char *new_fnp; /* pointer for scanning fn */ register char *bp; /* pointer to building fn_build */ FILE *f; /* opened file */ #if defined(UNIX_BSD) || defined(HAVE_SETEUID) int fd; /* file descriptor for opened file */ #endif /* reject non-root-based paths out of hand, though we shouldn't get any */ if (fn[0] != '/') { write_log(LOG_SYS|LOG_PANIC, "fopen_as_user: given non-root based path: %s", fn); errno = EACCES; return NULL; } #if defined(UNIX_BSD) || defined(HAVE_SETEUID) /* * Under BSD, it is easy if we are running as root. Just set the * effective ids to the passed uid and gid and call fopen. Then * set the effective ids back to root. Having independent seteuid * and setruid calls is great. * * NOTE: we assume that setgroups(0, (int *)NULL) has been called * to clear out any groups that may erroneously allow access * to the file. */ if (geteuid() == 0) { int save_gid = getegid(); /* order here is, oddly enough, mildly important */ (void) setegid(gid); (void) seteuid(uid); if (type[0] == 'a') { fd = open(fn, O_WRONLY|O_APPEND|O_CREAT, mode); if (fd < 0) { f = NULL; } else { f = fdopen(fd, type); } } else if (type[0] == 'w') { fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode); if (fd < 0) { f = NULL; } else { f = fdopen(fd, type); } } else { f = fopen(fn, type); } /* and it is important, again, here */ (void) seteuid(0); (void) setegid(save_gid); return f; } #endif /* UNIX_BSD || HAVE_SETEUID */ /* * now wasn't that nice and simple? Well, now we get to do * it the long way. We stat all of the directories to ensure * that search permission is allowed all the way down to the * file itself. Then, if type is "w" or "a", we chmod the * file, if it did not already exist (creating only if the * directory allowed write access, of course). * * NOTE: namei calls are not known to be cheap, though on a system * with a good namei cache, this may not be too bad. */ if (check_path) { fnp = fn; bp = fn_build = xmalloc(strlen(fn)); while (new_fnp = index(fnp, '/')) { while (fnp <= new_fnp) { *bp++ = *fnp++; } bp[0] = '\0'; /* since the name ends in /, it can only match directories */ if (stat(fn_build, &dir_stat) < 0) { /* directory doesn't exist, or we can't access it */ return NULL; } /* * should probably use some #define somewhere. However, * the octal numbers below are permission mode bits. */ if (! ((dir_stat.st_uid == uid && (dir_stat.st_mode & 0100)) || (dir_stat.st_gid == gid && (dir_stat.st_mode & 0010)) || (dir_stat.st_mode & 0001)) ) { errno = EACCES; return NULL; } } } /* made it that far, stat the file, itself */ if (stat(fn, &file_stat) >= 0) { /* file exists, make sure access is allowed, then open it */ if (type[0] == 'r') { /* check for read access */ if (! ((file_stat.st_uid == uid && (file_stat.st_mode & 0400)) || (file_stat.st_gid == gid && (file_stat.st_mode & 0040)) || (file_stat.st_mode & 0004)) ) { errno = EACCES; return NULL; } } else { /* check for write access */ if (! ((file_stat.st_uid == uid && (file_stat.st_mode & 0200)) || (file_stat.st_gid == gid && (file_stat.st_mode & 0020)) || (file_stat.st_mode & 0002)) ) { errno = EACCES; return NULL; } } return fopen(fn, type); } /* * file does not exist, if type is "r", this is an error, otherwise * the directory must be writable to creat the file. */ if (type[0] == 'r') { errno = EACCES; return NULL; } if (check_path) { if (! ((dir_stat.st_uid == uid && (dir_stat.st_mode & 0200)) || (dir_stat.st_gid == gid && (dir_stat.st_mode & 0020)) || (dir_stat.st_mode & 0002)) ) { errno = EACCES; return NULL; } } /* now creat the file and chown and chmod it */ f = fopen(fn, type); if (f == NULL) { return NULL; } /* * YAPTN - Yet Another Pass Through Namei() * The order here is significant for non-superusers under System V. */ (void) chmod(fn, mode); (void) chown(fn, uid, gid); return f; } /* * lock_file - lock a user's mailbox or other mail file * * return SUCCEED or FAIL. */ #ifdef lock_fd_wait /* * under 4.3BSD and some 4.2+ operating systems, flock(2) is used * to lock mailboxes or other mail files. FLOCK_MAILBOX should * be defined in os.h in this case. We assume that the file * descriptor points to the beginning of the file. */ int lock_file(fn, f) char *fn; /* name of file */ FILE *f; /* open file */ { long offset; int success; if (! flock_mailbox) { /* use filename-based locking for mailbox files */ return lock_file_by_name(fn); } offset = lseek(fileno(f), 0L, 1); (void) lseek(fileno(f), 0L, 0); success = lock_fd_wait(fileno(f)); (void) lseek(fileno(f), offset, 0); if (success < 0) { /* * This should never fail, but if it does, we will retry delivery * at a later time. */ return FAIL; } return SUCCEED; } #else /* not lock_fd_wait */ /* * if the lock_fd_wait macro is not available then we must always * use the V6-style file-locking. */ /*ARGSUSED*/ int lock_file(fn, f) char *fn; /* name of file */ FILE *f; /* open file */ { return lock_file_by_name(fn); } #endif /* not lock_fd_wait */ /* * V7-style or Xenix-style locking. * As far as I know, all flavors of UN*X that do not use 4.3BSD style * locking use this method. * * To lock a file named fn, create a file named fn.lock (or for Xenix, * /tmp/basename.mlk) and stuff the pid into it. If the file already * exisits, see if it is stale and remove it if so. If it was stale, * sleep for FNLOCK_INTERVAL and try to lock it again. Retry at most * FNLOCK_RETRIES times. On non-BSD systems, locking will not be done * if the basename for the file is more than 12 chars. The lenth * restriction does not apply to Xenix, because the basename is always * truncated to 10 chars, allowing sufficient space for the .mlk * suffix. * * on systems without O_EXCL for the open(2) call, creat is used * with a mode that does not allow writing. */ static int lock_file_by_name(fn) char *fn; /* name of file */ { int l_fd; /* open lock file */ char *l_fn; /* lock file name */ int retries; /* remaining retries */ char apid[BITS_PER_INT/3 + 1]; /* ASCII representation of pid */ if (fn[0] != '/') { /* * there should not be a way for the software to generate * a filename that does not begin with / */ panic(EX_SOFTWARE, "lock_by_name: non-rooted filename: %s", fn); /*NOTREACHED*/ } /* * will it be possible to create the lock file? On BSD systems, * the open call will tell us if the filename is too long. On * other systems, we need to check this ourselves before hand. * * On POSIX systems, the only way to know whether truncation * would occur is to call pathconf(). */ #if defined(POSIX_OS) { char *p = rindex(fn, '/') + 1; int no_trunc_flag; int name_max; if (strlen(p) > _POSIX_NAME_MAX - 2) { *(p - 1) = '\0'; no_trunc_flag = pathconf(fn, _PC_NO_TRUNC); if (! no_trunc_flag) { name_max = pathconf(fn, _PC_NAME_MAX); } *(p - 1) = '\0'; if (no_trunc_flag < 0 || name_max < 0) return FAIL; if (! no_trunc_flag && strlen(p) > name_max - 2) { /* * If there is not enough room to append a .l, then * always succeed the lock operation. This is dangerous * but there is probably nothing better to do. */ return SUCCEED; } } } #else /* not POSIX_OS */ #if !defined(UNIX_BSD) && !defined(UNIX_XENIX) { char *p = rindex(fn, '/') + 1; /* allow at least enough room for a trailing .l */ if (strlen(p) > DIRSIZ - 2) { /* * always succeed */ return SUCCEED; } } #endif /* not UNIX_BSD and not UNIX_XENIX */ #endif /* POSIX_OS */ /* * generate the lock filename */ #ifdef UNIX_XENIX { char *p = rindex(fn, '/') + 1; l_fn = xmalloc(sizeof("/tmp/.mlk") + 10); (void) sprintf(l_fn, "/tmp/%.10s.mlk", p); } #else /* not UNIX_XENIX */ l_fn = xmalloc(strlen(fn) + sizeof(".lock")); (void) sprintf(l_fn, "%s.lock", fn); #endif /* not UNIX_XENIX */ /* * try to create the lock file at most FNLOCK_RETRIES+1 times. * each time a lock fails, read a pid from the lock file and try * to remove the lock if that pid does not exist then sleep for * FNLOCK_INTERVAL and try again. */ for (retries = fnlock_retries; retries-- >= 0; sleep(fnlock_interval)) { #ifdef O_EXCL l_fd = open(l_fn, O_WRONLY|O_CREAT|O_EXCL, fnlock_mode); #else /* O_EXCL */ /* * if we can't open for exclusive creat, we will have to * use the creat call. */ l_fd = creat(l_fn, fnlock_mode & (~0222)); #endif /* O_EXCL */ if (l_fd < 0) { /* * examine why this failed */ #ifdef O_EXCL /* * open with O_EXCL returns this error if file exists */ if (errno == EEXIST) { check_stale(l_fn); /* remove stale lock files */ continue; } #else /* O_EXCL */ /* * creat returns the ambiguous EACCES if the file exists * and is not writable (the correct condition for a lock) */ if (errno == EACCES) { check_stale(l_fn); continue; } #endif /* O_EXCL */ /* * some other reason is preventing us from writing * the lock file, thus we won't bother retrying. */ xfree(l_fn); #ifdef UNIX_BSD if (errno == ENOENT) { /* * this is what BSD seems to return on basename too * long, so return SUCCEED in this case because * locking is not possible. Unfortunately this * error does not uniquely identify the problem. */ return SUCCEED; } #endif /* UNIX_BSD */ /* * for anything else, something strange is preventing us * creating the lock file. FAIL for now, we will try * again later. */ return FAIL; } break; } xfree(l_fn); /* don't need this any more */ /* * none of the attempts to create the lock file succeeded */ if (l_fd < 0) { return FAIL; } (void) sprintf(apid, "%d", getpid()); (void) write(l_fd, apid, strlen(apid)); (void) close(l_fd); return SUCCEED; } /* * check_stale - see if a lock file is stale. If so, remove it. */ static void check_stale(l_fn) char *l_fn; /* name of lock file */ { char buf[50]; int ct; int fd; int pid = 0; struct stat statbuf; fd = open(l_fn, O_RDONLY); if (fd < 0) { DEBUG2(DBG_DRIVER_MID, "%s: cannot open lock file: %s", l_fn, strerrno()); return; /* doesn't exist? */ } for (;;) { ct = read(fd, buf, sizeof(buf) - 1); if (ct == 0) { DEBUG1(DBG_DRIVER_MID, "%s: zero-length lock file ... ", l_fn); /* sleep 30 seconds and see if new data comes in */ (void) sleep(30); /* see if it has been removed */ (void) fstat(fd, &statbuf); /* fine if it went away */ if (statbuf.st_nlink == 0) { DEBUG(DBG_DRIVER_MID, "lock file went away\n"); (void) close(fd); return; } /* stale if still empty */ if (statbuf.st_size == 0) { if (statbuf.st_nlink > 0) { /* unlink if it still exists */ (void) unlink(l_fn); } DEBUG(DBG_DRIVER_MID, "still empty, removed\n"); (void) close(fd); return; } } else { #ifdef UNIX_XENIX /* For Xenix, simple existence is quite enough. */ DEBUG1(DBG_DRIVER_MID, "%s: valid lock file\n", l_fn); #else /* !Xenix */ buf[ct] = '\0'; (void) sscanf(buf, "%d", &pid); /* see if the pid exists */ if (kill(pid, 0) < 0 && errno == ESRCH) { /* process does not exist */ (void) fstat(fd, &statbuf); if (statbuf.st_nlink > 0) { /* remove the file if it still exists */ (void) unlink(l_fn); } DEBUG1(DBG_DRIVER_MID, "%s: stale lock file, removed\n", l_fn); } else { DEBUG1(DBG_DRIVER_MID, "%s: valid lock file\n", l_fn); } #endif /* !Xenix */ (void) close(fd); return; } } } /* * unlock_file - unlock a user's mailbox or other mail file * * we do not check to make sure we unlocked the file. What * could we do if an unlock failed? */ #ifdef lock_fd_wait /* * for 4.3BSD-style locking, just call flock to unlock the file */ void unlock_file(fn, f) char *fn; /* name of file */ FILE *f; /* open file */ { long offset; if (! flock_mailbox) { /* use the V6 locking protocol */ unlock_file_by_name(fn); return; } offset = lseek(fileno(f), 0L, 1); (void) lseek(fileno(f), 0L, 0); /* unlock from beginning to end */ unlock_fd_wait(fileno(f)); (void) lseek(fileno(f), offset, 0); } #else /* not lock_fd_wait */ /* * flock not available, so always call the V6-style unlock function. */ /*ARGSUSED*/ void unlock_file(fn, f) char *fn; /* name of file */ FILE *f; /* open file */ { unlock_file_by_name(fn); } #endif /* not lock_fd_wait */ /* * for V6-style locking remove the lock file. Be careful to * make sure that the name passed to unlink(2) won't unlink * the main file (i.e., use the same checks for basename size * used in creating the lock file) */ static void unlock_file_by_name(fn) char *fn; /* name of file */ { char *l_fn; /* lock file name */ if (fn[0] != '/') { /* * there should not be a way for the software to generate * a filename that does not begin with /, however, the panic * should have occured when lock_file was called. We will * check anyway, just to make us feel better. */ panic(EX_SOFTWARE, "unlock_file: non-rooted filename: %s", fn); /*NOTREACHED*/ } /* * was it possible to create the lock file? On BSD systems, * the unlink will fail if the lock file name is too long. On * other systems, we need to check this ourselves before hand. * * On POSIX systems, the only way to know this is to call * pathconf(). */ #if defined(POSIX_OS) { char *p = rindex(fn, '/') + 1; int no_trunc_flag; int name_max; if (strlen(p) > _POSIX_NAME_MAX - 2) { *(p - 1) = '\0'; no_trunc_flag = pathconf(fn, _PC_NO_TRUNC); if (! no_trunc_flag) { name_max = pathconf(fn, _PC_NAME_MAX); } *(p - 1) = '\0'; if (no_trunc_flag < 0 || name_max < 0) return; /* directory not found? */ if (! no_trunc_flag && strlen(p) > name_max - 2) { /* * If there is not enough room to append a .l, then * the lock operation should not have created a * lock file. */ return; } } } #else /* not POSIX_OS */ #if !defined(UNIX_BSD) && !defined(UNIX_XENIX) { char *p = rindex(fn, '/') + 1; /* allow at least enough room for a trailing .l */ if (strlen(p) > DIRSIZ - 2) { /* * no lock file could have been created so don't try * to remove one. */ return; } } #endif /* not UNIX_BSD and not UNIX_XENIX */ #endif /* POSIX_OS */ /* * generate the lock filename */ #ifdef UNIX_XENIX { char *p = rindex(fn, '/') + 1; l_fn = xmalloc(sizeof("/tmp/.mlk") + 10); (void) sprintf(l_fn, "/tmp/%.10s.mlk", p); } #else /* not UNIX_XENIX */ l_fn = xmalloc(strlen(fn) + sizeof(".lock")); (void) sprintf(l_fn, "%s.lock", fn); #endif /* not UNIX_XENIX */ /* * remove the lock file and clean up */ (void) unlink(l_fn); xfree(l_fn); } /* * compute_hostname - compute the name of the local host * * return NULL if we are on an operating system that does not know about * hostnames. */ #ifdef HAVE_GETHOSTNAME char * compute_hostname() { register char *p; static char *hostname; # ifdef GETHOSTNAME_USE_PTR int len; # endif /* * My man page says that 255 chars (plus nul byte) is the limit * on length of the local host name. There appears to be no * #define for it in 4.2BSD. */ hostname = xmalloc(256); # ifdef GETHOSTNAME_USE_PTR len = 256; (void) gethostname(hostname, &len); # else (void) gethostname(hostname, 256); # endif /* * Some systems return the entire hostname, including the domain. * In this case, the primary hostname should be set to this name, * though for now, just truncate off the domain. */ if ((p = index(hostname, '.')) != NULL) { *p = '\0'; } /* though we don't need to continue wasting all that space */ hostname = xrealloc(hostname, strlen(hostname) + 1); return hostname; } #else /* not HAVE_GETHOSTNAME */ # ifdef HAVE_UNAME char * compute_hostname() { static struct utsname utsname; (void) uname(&utsname); /* Is the sysname tag used for something interesting? */ return utsname.nodename; } # else /* not HAVE_UNAME */ # ifdef SITENAME_FILE /* sitename is stored in a file */ char * compute_hostname() { static struct str hostname; static int inited = FALSE; register int c; FILE *f; if (inited) { return hostname.p; } inited = TRUE; STR_INIT(&hostname); f = fopen(SITENAME_FILE, "r"); while ((c = getc(f)) != EOF && c != '\n') { STR_NEXT(&hostname, c); } STR_NEXT(&hostname, '\0'); STR_DONE(&hostname); return hostname.p; } # else /* not SITENAME_FILE */ char * compute_hostname() { /* * perhaps we should call uuname -l rather than punting. */ panic(EX_SOFTWARE, "the local host name is not computable and is not configured"); /*NOTREACHED*/ } # endif /* not SITENAME_FILE */ # endif /* not HAVE_UNAME */ #endif /* not HAVE_GETHOSTNAME */ /* * open_child - create a child process and open pipes to it and exec * * open_child creates a child process, with possible read and write * pipes to the child process's stdin and stdout/stderr. Setuid and * setgid are called in the child process to change the uid/gid to * whichever uid/gid are given to open_child. * * open_child does nothing with signals in the parent process. * Only signal behaviors modified by exec will be changed in the * child process. If more complex signal behavior desired, call * with argv == NULL and handle signals and the exec in the caller * routine within the child process. * * inputs: * argv - a vector suitable for passing to execve. argv[0] is * given as the program to exec. If argv is NULL, then * do a return instead of an exec. In this case as well, * never to a vfork(), always use fork(). * envp - a vector of environment variables suitable for passing * to execve. If envp is null and CHILD_MINENV is not * set in flags, then the parents environment will be * passed to the child. * in - a pointer to a FILE* variable. If non-NULL, a pipe * is created, with the write-end returned in this * variable and with the read-end tied to the stdin of * the child process. * out - a pointer to a FILE* variable. If non-NULL, a pipe * is created, with thre read-end returned in this * variable and with the write-end tied to the stdout * of the child process. * errfd - if errfd >= 0 then it sepecifies a file descriptor * to duplicate to the stdout and/or stderr of the * child. If out != NULL, errfd is only dup'd to the * stderr. * flags - a bitmask of flags affecting open_child's operation. * Flags are: * * CHILD_DUPERR - duplicate stdout to stderr in the * child process. * CHILD_DEVNULL - open "/dev/null" and associate it * with stdin and/or stdout in the * child process, if no in and/or out * variable is specified. * CHILD_RETRY - retry fork() operation up to FORK_RETRIES * times at FORK_INTERVAL second intervals, * if fork returns EAGAIN. * CHILD_MINENV - give the child process a very minimal * environment. This is overridden by giving * an explicit environment in envp. * CHILD_NOCLOSE - don't close file descriptors. If this is * not set, close all file descriptors * other than stdin/stdout/stderr. * * uid - do a setuid(uid) in the child * gid - do a setgid(gid) in the child * * output: * pid of child process, or 0 if in child process (only if argv==NULL). * Return EOF if pipe() or fork() failed. */ int open_child(argv, envp, in, out, errfd, flags, uid, gid) char **argv; /* arg vector for execve */ char **envp; /* environment vector for execve */ FILE **in; /* make a stdin file pointer */ FILE **out; /* make a stdout file pointer */ int errfd; /* fd to use for stdout/stderr */ int flags; /* flags affecting operation */ int uid; /* user ID for child process */ int gid; /* group ID for child process */ { int stdin_fd[2]; /* fds from pipe(2) for child stdin */ int stdout_fd[2]; /* fds from pipe(2) for child stdout */ /* remember, fd[0] is the read descriptor, fd[1] is the write descriptor */ int retries = FORK_RETRIES; /* countdown of retries */ int pid; /* pid from fork() */ static char *min_env[] = { #ifdef CHILD_ENVIRON CHILD_ENVIRON, #else /* CHILD_ENVIRON */ "PATH=/bin:/usr/bin", "SHELL=/bin/sh", "HOME=/", #endif /* CHILD_ENVIRON */ NULL, /* leave space for timezone */ NULL, /* end of environment */ }; if (in && pipe(stdin_fd) < 0) { return EOF; } if (out && pipe(stdout_fd) < 0) { /* free resources */ (void) close(stdin_fd[0]); (void) close(stdin_fd[1]); return EOF; } /* keep trying to create a fork until we succeed or retries == 0 */ while ( #if defined(UNIX_BSD) || defined(HAVE_VFORK) (pid = (argv? vfork(): fork())) < 0 && #else /* not UNIX_BSD && not HAVE_VFORK */ (pid = fork()) < 0 && #endif /* UNIX_BSD || HAVE_VFORK */ errno == EAGAIN && --retries >= 0) { (void) sleep(FORK_INTERVAL); /* sleep between retries */ } if (pid < 0) { /* free resources */ (void) close(stdin_fd[0]); (void) close(stdin_fd[1]); (void) close(stdout_fd[0]); (void) close(stdout_fd[1]); return EOF; } if (pid == 0) { /* in child process */ /* close unnecessary file descriptors */ if (in) { (void) close(stdin_fd[1]); } if (out) { (void) close(stdout_fd[0]); } /* if errfd == 0, watch out for the code below */ if (errfd == 0 || errfd == 1) { int new_errfd; /* search for a descriptor we don't care about */ for (new_errfd = 3; new_errfd < 10; new_errfd++) { if (in && new_errfd == stdin_fd[0] || out && new_errfd == stdout_fd[1]) { continue; } } (void) dup2(errfd, new_errfd); (void) close(errfd); errfd = new_errfd; } if (in || (flags & CHILD_DEVNULL)) { /* * setup the child's stdin */ if (in) { /* pipe from parent process */ if (stdin_fd[0] != 0) { (void) dup2(stdin_fd[0], 0); (void) close(stdin_fd[0]); } } else if (flags & CHILD_DEVNULL) { /* * XXX - we rely on pipe() never returning 0 as the * write file descriptor, or this could close * stdout_fd[1], which would not be nice. */ /* open /dev/null as the stdin */ (void) close(0); if (open("/dev/null", O_RDONLY) < 0) { /* uggh! failed to open /dev/null, quit */ _exit(EX_OSFILE); } } } if (out || errfd >= 0 || (flags & CHILD_DEVNULL)) { /* * setup the child's stdout */ if (out) { /* pipe to parent process */ if (stdout_fd[1] != 1) { (void) dup2(stdout_fd[1], 1); (void) close(stdout_fd[1]); } } else if (errfd >= 0) { /* use the given fd for stdout */ (void) dup2(errfd, 1); } else if (flags & CHILD_DEVNULL) { /* open /dev/null as stdout */ (void) close(1); if (open("/dev/null", O_WRONLY) < 0) { /* uggh! failed to open /dev/null, quit */ _exit(EX_OSFILE); } } } if (errfd >= 0) { /* use this fd as the stderr */ if (errfd != 2) { (void) dup2(errfd, 2); (void) close(errfd); } } else if (flags & CHILD_DUPERR) { /* duplicate stdout to get the stderr */ (void) dup2(1, 2); } else { (void) close(2); if (open("/dev/null", O_WRONLY) < 0) { /* uggh! failed to open /dev/null, quit */ _exit(EX_OSFILE); } } /* close all unnecessary file descriptors */ if ( !(flags & CHILD_NOCLOSE) ) { close_all(); } /* change uid and gid, if we can, don't bother to check */ (void) setgid(gid); (void) setuid(uid); if (argv) { /* execute the program */ if (envp) { (void) execve(argv[0], argv, envp); } else if (flags & CHILD_MINENV) { /* pass the TZ environment variable on non-BSD systems */ char *tz = getenv("TZ"); if (tz) { char *tzenv = xmalloc(strlen(tz) + 4); strcpy(tzenv, "TZ="); strcat(tzenv, tz); min_env[3] = tzenv; } (void) execve(argv[0], argv, min_env); } else { (void) execv(argv[0], argv); } /* Oh well, all this work and we can't even exec the file */ _exit(EX_OSFILE); } } else { /* in parent process */ if (in) { /* close the child stdin, return the write-channel */ (void) close(stdin_fd[0]); *in = fdopen(stdin_fd[1], "w"); } if (out) { /* close the child stdout, return the read-channel */ (void) close(stdout_fd[1]); *out = fdopen(stdout_fd[0], "r"); } } return pid; } /* * close_child - close down the child process started with open_child. * * if non-zero file pointers are passed, the given files are closed. * Then, wait for the specified pid to exit and return its status. * * NOTE: we do not keep the exit status of processes other than the * one we are waiting. Thus, only one process should be open * at a time by open_child, unless the caller wishes to handle * the performing the waits itself. * * return EOF, on error, or exit status. */ int close_child(in, out, pid) FILE *in; /* pipe to child's stdin */ FILE *out; /* pipe to child's stdout */ int pid; /* pid of child process */ { int i; /* return value from wait */ int status; /* status from wait */ if (in) { (void) fclose(in); /* close the pipe to stdin */ } if (out) { (void) fclose(out); /* close the pipe to stdout */ } /* wait for the child process to die */ while ((i = wait(&status)) != pid && i >= 0) ; if (i < 0) { /* failed to find the child process */ return FAIL; } return status; /* everything went okay */ } /* * close_all - close all file descriptors other than 0, 1 and 2 * * At several key points smail needs to know that no extra file descriptors * being used, such as when execing other processes (to ensure that * child processes cannot read or write protected information left open in * smail) and at startup (to prevent smail running out of file descriptors). */ void close_all() { register int i; int max_nfd; /* so just how many file descriptors do we have? */ #ifdef POSIX_OS max_nfd = sysconf(_SC_OPEN_MAX); #else /* not POSIX_OS */ # ifdef OPEN_MAX max_nfd = OPEN_MAX; # else /* not POSIX_OS && not OPEN_MAX */ # ifdef NOFILE max_nfd = NOFILE; # else /* not POSIX_OS && not OPEN_MAX && not NOFILE */ max_nfd = 20; # endif # endif #endif for (i = 3; i < max_nfd; i++) (void) close(i); } #ifndef HAVE_RENAME /* * rename - emulate the BSD/System V.3 rename(2) system call * * This subroutine is not atomic, so functions which are worried about * consistency across system failures or in the face of multiple processes * should consider the situation when HAVE_RENAME is not defined. * * The errno returned will not always correspond to what would have * been produced by a true rename(2) system call. */ int rename(from, to) char *from; /* old file name */ char *to; /* new file name */ { if (unlink(to) && errno != ENOENT) { /* could not unlink `to', though it may exist */ return -1; } if (link(from, to) < 0) { /* failed to link, however, an existing `to' file was removed */ return -1; } if (unlink(from) < 0) { /* could not unlink, the file exists under two names */ return -1; } return 0; } #endif /* HAVE_RENAME */ #if !defined(HAVE_MKDIR) /* * for OS's that lack a mkdir system call, call the mkdir program */ int mkdir(dir, mode) char *dir; int mode; { static char *mkdir_argv[] = { "/bin/mkdir", NULL, /* space for directory name */ NULL, }; /* set the umask so that the directory will have the correct perms */ int oumask = umask((~mode)&0777); int pid; mkdir_argv[1] = dir; /* start up the mkdir program */ pid = open_child(mkdir_argv, (char **)NULL, (FILE **)NULL, (FILE **)NULL, -1, CHILD_DEVNULL, 0, 0); (void) umask(oumask); return close_child((FILE *)NULL, (FILE *)NULL, pid); } #endif /* !defined(HAVE_MKDIR) */ #ifndef HAVE_DUP2 /* * dup2 - this provides the dup2 function as described by the Posix Draft * for those sites which do not have it. * * Mark Colburn (mark@jhereg.mn.org) */ int dup2(fildes, fildes2) int fildes, fildes2; { int fid; if (fcntl(fildes, F_GETFL, 0) == -1) return(EBADF); if (fildes != fildes2) close(fildes2); fid = fcntl(fildes, F_DUPFD, fildes2); return(fid); } #endif /* HAVE_DUP2 */ #ifndef HAVE_READDIR /* * for OS's that lack the directory facilities, emulate */ struct direct_with_null { struct direct d; int trailing_null; /* make sure name ends in a nul byte */ }; typedef FILE DIR; #define opendir(dn) (fopen(dn, "r")) #define closedir(dn) (fclose(dn)) static struct direct * readdir(dirp) DIR *dirp; { static struct direct_with_null ret; ret.trailing_null = 0; do { if (fread(&ret.d, sizeof(ret.d), 1, (FILE *)dirp) < 1) { return NULL; } } while (ret.d.d_ino == 0); return &ret.d; } #endif /* HAVE_READDIR */ /* * scan_dir - scan through a directory, looking for filenames * * if given a non-NULL directory argument, opens the directory and * returns the first file, or NULL if it contains no files. * successive calls, with a NULL argument, return successive files * in the directory. On the call after the last directory, a NULL * is returned and the directory is closed. * * scan_dir returns static data which is reused on subsequent calls. */ char * scan_dir(dn) char *dn; /* directory name, or NULL */ { static DIR *dirp = NULL; /* opened directory */ struct direct *dp; /* current directory entry */ if (dn) { dirp = opendir(dn); } if (dirp == NULL) { return NULL; /* no directory, so no entries */ } dp = readdir(dirp); if (dp == NULL) { (void) closedir(dirp); return NULL; } return dp->d_name; } #ifdef USE_FCNTL_RD_LOCK /* * fcntl_rd_lock - get a shared lock using a System V-style fcntl. */ int fcntl_rd_lock(fd) int fd; { struct flock l; l.l_type = F_RDLCK; l.l_whence = 0; l.l_start = 0L; l.l_len = 0L; if (fcntl(fd, F_SETLKW, &l) < 0) { return FAIL; } return SUCCEED; } #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.