rcsutil.c revision 21673
119304Speter/* RCS utility functions */ 219304Speter 319304Speter/* Copyright 1982, 1988, 1989 Walter Tichy 419304Speter Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 519304Speter Distributed under license by the Free Software Foundation, Inc. 619304Speter 719304SpeterThis file is part of RCS. 819304Speter 919304SpeterRCS is free software; you can redistribute it and/or modify 1019304Speterit under the terms of the GNU General Public License as published by 1119304Speterthe Free Software Foundation; either version 2, or (at your option) 1219304Speterany later version. 13254225Speter 1419304SpeterRCS is distributed in the hope that it will be useful, 1519304Speterbut WITHOUT ANY WARRANTY; without even the implied warranty of 1619304SpeterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1719304SpeterGNU General Public License for more details. 1819304Speter 1919304SpeterYou should have received a copy of the GNU General Public License 2019304Speteralong with RCS; see the file COPYING. 2119304SpeterIf not, write to the Free Software Foundation, 2219304Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2319304Speter 24254225SpeterReport problems and direct all questions to: 2519304Speter 2619304Speter rcs-bugs@cs.purdue.edu 2719304Speter 2819304Speter*/ 2919304Speter 3019304Speter 3119304Speter 3219304Speter 3319304Speter/* 3419304Speter * Revision 5.20 1995/06/16 06:19:24 eggert 3519304Speter * (catchsig): Remove `return'. 3619304Speter * Update FSF address. 3719304Speter * 3819304Speter * Revision 5.19 1995/06/02 18:19:00 eggert 3919304Speter * (catchsigaction): New name for `catchsig', for sa_sigaction signature. 4019304Speter * Use nRCS even if !has_psiginfo, to remove unused variable warning. 4119304Speter * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. 4219304Speter * Use ENOTSUP only if defined. 4319304Speter * 4419304Speter * Revision 5.18 1995/06/01 16:23:43 eggert 4519304Speter * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo, 4619304Speter * to determine whether to use SA_SIGINFO feature, 4719304Speter * but also check at runtime whether the feature works. 4819304Speter * (catchsig): If an mmap_signal occurs, report the affected file name. 4919304Speter * (unsupported_SA_SIGINFO, accessName): New variables. 5019304Speter * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler. 5119304Speter * If SA_SIGINFO fails, fall back on sa_handler method. 5219304Speter * 5319304Speter * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions. 5419304Speter * (concatenate): Remove. 5519304Speter * 5619304Speter * (runv): Work around bad_wait_if_SIGCHLD_ignored bug. 5719304Speter * Remove reference to OPEN_O_WORK. 5819304Speter * 5919304Speter * Revision 5.17 1994/03/20 04:52:58 eggert 6019304Speter * Specify subprocess input via file descriptor, not file name. 6119304Speter * Avoid messing with I/O buffers in the child process. 6219304Speter * Define dup in terms of F_DUPFD if it exists. 6319304Speter * Move setmtime to rcsedit.c. Remove lint. 6419304Speter * 6519304Speter * Revision 5.16 1993/11/09 17:40:15 eggert 6619304Speter * -V now prints version on stdout and exits. 6719304Speter * 6819304Speter * Revision 5.15 1993/11/03 17:42:27 eggert 6919304Speter * Use psiginfo and setreuid if available. Move date2str to maketime.c. 7019304Speter * 71254225Speter * Revision 5.14 1992/07/28 16:12:44 eggert 72254225Speter * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug. 7319304Speter * Add mmap_signal, which minimizes signal handling for non-mmap hosts. 7419304Speter * 7519304Speter * Revision 5.13 1992/02/17 23:02:28 eggert 7619304Speter * Work around NFS mmap SIGBUS problem. Add -T support. 7719304Speter * 7819304Speter * Revision 5.12 1992/01/24 18:44:19 eggert 7919304Speter * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint 80254225Speter * 81254225Speter * Revision 5.11 1992/01/06 02:42:34 eggert 82254225Speter * O_BINARY -> OPEN_O_WORK 83254225Speter * while (E) ; -> while (E) continue; 84254225Speter * 85254225Speter * Revision 5.10 1991/10/07 17:32:46 eggert 86254225Speter * Support piece tables even if !has_mmap. 87254225Speter * 88254225Speter * Revision 5.9 1991/08/19 03:13:55 eggert 8919304Speter * Add spawn() support. Explicate assumptions about getting invoker's name. 9019304Speter * Standardize user-visible dates. Tune. 9119304Speter * 9219304Speter * Revision 5.8 1991/04/21 11:58:30 eggert 9319304Speter * Plug setuid security hole. 9419304Speter * 9519304Speter * Revision 5.6 1991/02/26 17:48:39 eggert 96254225Speter * Fix setuid bug. Use fread, fwrite more portably. 97254225Speter * Support waitpid. Don't assume -1 is acceptable to W* macros. 98254225Speter * strsave -> str_save (DG/UX name clash) 9919304Speter * 10019304Speter * Revision 5.5 1990/12/04 05:18:49 eggert 10119304Speter * Don't output a blank line after a signal diagnostic. 10219304Speter * Use -I for prompts and -q for diagnostics. 10319304Speter * 10419304Speter * Revision 5.4 1990/11/01 05:03:53 eggert 10519304Speter * Remove unneeded setid check. Add awrite(), fremember(). 10619304Speter * 10719304Speter * Revision 5.3 1990/10/06 00:16:45 eggert 10819304Speter * Don't fread F if feof(F). 10919304Speter * 11019304Speter * Revision 5.2 1990/09/04 08:02:31 eggert 11119304Speter * Store fread()'s result in an fread_type object. 11219304Speter * 11319304Speter * Revision 5.1 1990/08/29 07:14:07 eggert 11419304Speter * Declare getpwuid() more carefully. 11519304Speter * 11619304Speter * Revision 5.0 1990/08/22 08:13:46 eggert 11719304Speter * Add setuid support. Permit multiple locks per user. 11819304Speter * Remove compile-time limits; use malloc instead. 11919304Speter * Switch to GMT. Permit dates past 1999/12/31. 12019304Speter * Add -V. Remove snooping. Ansify and Posixate. 12119304Speter * Tune. Some USG hosts define NSIG but not sys_siglist. 12219304Speter * Don't run /bin/sh if it's hopeless. 12319304Speter * Don't leave garbage behind if the output is an empty pipe. 12419304Speter * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. 12519304Speter * 12619304Speter * Revision 4.6 89/05/01 15:13:40 narten 12719304Speter * changed copyright header to reflect current distribution rules 12819304Speter * 12919304Speter * Revision 4.5 88/11/08 16:01:02 narten 13019304Speter * corrected use of varargs routines 13119304Speter * 132254225Speter * Revision 4.4 88/08/09 19:13:24 eggert 133254225Speter * Check for memory exhaustion. 134254225Speter * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. 13519304Speter * Use execv(), not system(); yield exit status like diff(1)'s. 13619304Speter * 13719304Speter * Revision 4.3 87/10/18 10:40:22 narten 13819304Speter * Updating version numbers. Changes relative to 1.1 actually 13919304Speter * relative to 4.1 14019304Speter * 14119304Speter * Revision 1.3 87/09/24 14:01:01 narten 14219304Speter * Sources now pass through lint (if you ignore printf/sprintf/fprintf 14319304Speter * warnings) 14419304Speter * 14519304Speter * Revision 1.2 87/03/27 14:22:43 jenkins 14619304Speter * Port to suns 14719304Speter * 14819304Speter * Revision 4.1 83/05/10 15:53:13 wft 14919304Speter * Added getcaller() and findlock(). 15019304Speter * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal 15119304Speter * (needed for background jobs in older shells). Added restoreints(). 15219304Speter * Removed printing of full RCS path from logcommand(). 15319304Speter * 15419304Speter * Revision 3.8 83/02/15 15:41:49 wft 15519304Speter * Added routine fastcopy() to copy remainder of a file in blocks. 15619304Speter * 15719304Speter * Revision 3.7 82/12/24 15:25:19 wft 15819304Speter * added catchints(), ignoreints() for catching and ingnoring interrupts; 15919304Speter * fixed catchsig(). 16019304Speter * 16119304Speter * Revision 3.6 82/12/08 21:52:05 wft 162254225Speter * Using DATEFORM to format dates. 16319304Speter * 16419304Speter * Revision 3.5 82/12/04 18:20:49 wft 16519304Speter * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update 16619304Speter * lockedby-field. 16719304Speter * 16819304Speter * Revision 3.4 82/12/03 17:17:43 wft 16919304Speter * Added check to addlock() ensuring only one lock per person. 17019304Speter * Addlock also returns a pointer to the lock created. Deleted fancydate(). 17119304Speter * 17219304Speter * Revision 3.3 82/11/27 12:24:37 wft 17319304Speter * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. 17419304Speter * Introduced macro SNOOP so that snoop can be placed in directory other than 17519304Speter * TARGETDIR. Changed %02d to %.2d for compatibility reasons. 17619304Speter * 17719304Speter * Revision 3.2 82/10/18 21:15:11 wft 17819304Speter * added function getfullRCSname(). 17919304Speter * 18019304Speter * Revision 3.1 82/10/13 16:17:37 wft 18119304Speter * Cleanup message is now suppressed in quiet mode. 18219304Speter */ 18319304Speter 18419304Speter 18519304Speter 18619304Speter 18719304Speter#include "rcsbase.h" 18819304Speter 189254225SpeterlibId(utilId, "$FreeBSD: head/gnu/usr.bin/rcs/lib/rcsutil.c 21673 1997-01-14 07:20:47Z jkh $") 190254225Speter 191254225Speter#if !has_memcmp 19219304Speter int 19319304Spetermemcmp(s1, s2, n) 19419304Speter void const *s1, *s2; 19519304Speter size_t n; 19619304Speter{ 197254225Speter register unsigned char const 198254225Speter *p1 = (unsigned char const*)s1, 19919304Speter *p2 = (unsigned char const*)s2; 20019304Speter register size_t i = n; 20119304Speter register int r = 0; 20219304Speter while (i-- && !(r = (*p1++ - *p2++))) 20319304Speter ; 20419304Speter return r; 20519304Speter} 20619304Speter#endif 20719304Speter 20819304Speter#if !has_memcpy 20919304Speter void * 21019304Spetermemcpy(s1, s2, n) 21119304Speter void *s1; 21219304Speter void const *s2; 21319304Speter size_t n; 21419304Speter{ 21519304Speter register char *p1 = (char*)s1; 21619304Speter register char const *p2 = (char const*)s2; 21719304Speter while (n--) 21819304Speter *p1++ = *p2++; 21919304Speter return s1; 22019304Speter} 22119304Speter#endif 22219304Speter 22319304Speter#if RCS_lint 22419304Speter malloc_type lintalloc; 22519304Speter#endif 22619304Speter 227254225Speter/* 228254225Speter * list of blocks allocated with ftestalloc() 229254225Speter * These blocks can be freed by ffree when we're done with the current file. 230254225Speter * We could put the free block inside struct alloclist, rather than a pointer 23119304Speter * to the free block, but that would be less portable. 23219304Speter */ 23319304Speterstruct alloclist { 23419304Speter malloc_type alloc; 235254225Speter struct alloclist *nextalloc; 236254225Speter}; 23719304Speterstatic struct alloclist *alloced; 23819304Speter 23919304Speter 24019304Speter static malloc_type okalloc P((malloc_type)); 24119304Speter static malloc_type 24219304Speterokalloc(p) 24319304Speter malloc_type p; 24419304Speter{ 24519304Speter if (!p) 24619304Speter faterror("out of memory"); 24719304Speter return p; 24819304Speter} 24919304Speter 25019304Speter malloc_type 25119304Spetertestalloc(size) 25219304Speter size_t size; 25319304Speter/* Allocate a block, testing that the allocation succeeded. */ 25419304Speter{ 25519304Speter return okalloc(malloc(size)); 25619304Speter} 25719304Speter 25819304Speter malloc_type 25919304Spetertestrealloc(ptr, size) 26019304Speter malloc_type ptr; 26119304Speter size_t size; 26219304Speter/* Reallocate a block, testing that the allocation succeeded. */ 26319304Speter{ 26419304Speter return okalloc(realloc(ptr, size)); 26519304Speter} 26619304Speter 26719304Speter malloc_type 26819304Speterfremember(ptr) 26919304Speter malloc_type ptr; 270254225Speter/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ 27119304Speter{ 27219304Speter register struct alloclist *q = talloc(struct alloclist); 27319304Speter q->nextalloc = alloced; 27419304Speter alloced = q; 275254225Speter return q->alloc = ptr; 276254225Speter} 277254225Speter 27819304Speter malloc_type 27919304Speterftestalloc(size) 280254225Speter size_t size; 28119304Speter/* Allocate a block, putting it in 'alloced' so it can be freed later. */ 282254225Speter{ 283254225Speter return fremember(testalloc(size)); 28419304Speter} 28519304Speter 286254225Speter void 28719304Speterffree() 28819304Speter/* Free all blocks allocated with ftestalloc(). */ 28919304Speter{ 29019304Speter register struct alloclist *p, *q; 29119304Speter for (p = alloced; p; p = q) { 29219304Speter q = p->nextalloc; 293254225Speter tfree(p->alloc); 29419304Speter tfree(p); 29519304Speter } 29619304Speter alloced = 0; 29719304Speter} 29819304Speter 29919304Speter void 30019304Speterffree1(f) 30119304Speter register char const *f; 30219304Speter/* Free the block f, which was allocated by ftestalloc. */ 30319304Speter{ 30419304Speter register struct alloclist *p, **a = &alloced; 30519304Speter 30619304Speter while ((p = *a)->alloc != f) 30719304Speter a = &p->nextalloc; 30819304Speter *a = p->nextalloc; 30919304Speter tfree(p->alloc); 31019304Speter tfree(p); 31119304Speter} 31219304Speter 31319304Speter char * 31419304Speterstr_save(s) 31519304Speter char const *s; 31619304Speter/* Save s in permanently allocated storage. */ 31719304Speter{ 31819304Speter return strcpy(tnalloc(char, strlen(s)+1), s); 31919304Speter} 32019304Speter 32119304Speter char * 32219304Speterfstr_save(s) 32319304Speter char const *s; 32419304Speter/* Save s in storage that will be deallocated when we're done with this file. */ 32519304Speter{ 32619304Speter return strcpy(ftnalloc(char, strlen(s)+1), s); 32719304Speter} 32819304Speter 32919304Speter char * 330254225Spetercgetenv(name) 331254225Speter char const *name; 332254225Speter/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ 33319304Speter{ 33419304Speter register char *p; 33519304Speter 33619304Speter return (p=getenv(name)) ? str_save(p) : p; 33719304Speter} 33819304Speter 33919304Speter char const * 34019304Spetergetusername(suspicious) 34119304Speter int suspicious; 34219304Speter/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */ 34319304Speter{ 34419304Speter static char *name; 34519304Speter 34619304Speter if (!name) { 34719304Speter if ( 348254225Speter /* Prefer getenv() unless suspicious; it's much faster. */ 34919304Speter# if getlogin_is_secure 35019304Speter (suspicious 35119304Speter || ( 35219304Speter !(name = cgetenv("LOGNAME")) 35319304Speter && !(name = cgetenv("USER")) 35419304Speter )) 35519304Speter && !(name = getlogin()) 35619304Speter# else 35719304Speter suspicious 35819304Speter || ( 35919304Speter !(name = cgetenv("LOGNAME")) 36019304Speter && !(name = cgetenv("USER")) 36119304Speter && !(name = getlogin()) 36219304Speter ) 36319304Speter# endif 36419304Speter ) { 36519304Speter#if has_getuid && has_getpwuid 36619304Speter struct passwd const *pw = getpwuid(ruid()); 36719304Speter if (!pw) 36819304Speter faterror("no password entry for userid %lu", 36919304Speter (unsigned long)ruid() 37019304Speter ); 37119304Speter name = pw->pw_name; 37219304Speter#else 37319304Speter#if has_setuid 37419304Speter faterror("setuid not supported"); 37519304Speter#else 376254225Speter faterror("Who are you? Please setenv LOGNAME."); 377254225Speter#endif 378254225Speter#endif 37919304Speter } 38019304Speter checksid(name); 38119304Speter } 38219304Speter return name; 38319304Speter} 38419304Speter 38519304Speter 38619304Speter 38719304Speter 38819304Speter#if has_signal 38919304Speter 39019304Speter/* 39119304Speter * Signal handling 39219304Speter * 39319304Speter * Standard C places too many restrictions on signal handlers. 39419304Speter * We obey as many of them as we can. 39519304Speter * Posix places fewer restrictions, and we are Posix-compatible here. 39619304Speter */ 39719304Speter 39819304Speterstatic sig_atomic_t volatile heldsignal, holdlevel; 39919304Speter#ifdef SA_SIGINFO 40019304Speter static int unsupported_SA_SIGINFO; 40119304Speter static siginfo_t bufsiginfo; 40219304Speter static siginfo_t *volatile heldsiginfo; 40319304Speter#endif 40419304Speter 40519304Speter 40619304Speter#if has_NFS && has_mmap && large_memory && mmap_signal 40719304Speter static char const *accessName; 40819304Speter 40919304Speter void 41019304Speter readAccessFilenameBuffer(filename, p) 41119304Speter char const *filename; 41219304Speter unsigned char const *p; 41319304Speter { 41419304Speter unsigned char volatile t; 41519304Speter accessName = filename; 41619304Speter t = *p; 41719304Speter accessName = 0; 41819304Speter } 41919304Speter#else 42019304Speter# define accessName ((char const *) 0) 42119304Speter#endif 42219304Speter 42319304Speter 42419304Speter#if !has_psignal 42519304Speter 42619304Speter# define psignal my_psignal 42719304Speter static void my_psignal P((int,char const*)); 42819304Speter static void 42919304Spetermy_psignal(sig, s) 43019304Speter int sig; 43119304Speter char const *s; 432254225Speter{ 433254225Speter char const *sname = "Unknown signal"; 43419304Speter# if has_sys_siglist && defined(NSIG) 43519304Speter if ((unsigned)sig < NSIG) 43619304Speter sname = sys_siglist[sig]; 43719304Speter# else 43819304Speter switch (sig) { 43919304Speter# ifdef SIGHUP 44019304Speter case SIGHUP: sname = "Hangup"; break; 44119304Speter# endif 442254225Speter# ifdef SIGINT 443254225Speter case SIGINT: sname = "Interrupt"; break; 44419304Speter# endif 44519304Speter# ifdef SIGPIPE 44619304Speter case SIGPIPE: sname = "Broken pipe"; break; 44719304Speter# endif 44819304Speter# ifdef SIGQUIT 44919304Speter case SIGQUIT: sname = "Quit"; break; 45019304Speter# endif 45119304Speter# ifdef SIGTERM 45219304Speter case SIGTERM: sname = "Terminated"; break; 45319304Speter# endif 45419304Speter# ifdef SIGXCPU 45519304Speter case SIGXCPU: sname = "Cputime limit exceeded"; break; 45619304Speter# endif 45719304Speter# ifdef SIGXFSZ 45819304Speter case SIGXFSZ: sname = "Filesize limit exceeded"; break; 45919304Speter# endif 46019304Speter# if has_mmap && large_memory 46119304Speter# if defined(SIGBUS) && mmap_signal==SIGBUS 46219304Speter case SIGBUS: sname = "Bus error"; break; 46319304Speter# endif 46419304Speter# if defined(SIGSEGV) && mmap_signal==SIGSEGV 46519304Speter case SIGSEGV: sname = "Segmentation fault"; break; 46619304Speter# endif 46719304Speter# endif 46819304Speter } 46919304Speter# endif 47019304Speter 47119304Speter /* Avoid calling sprintf etc., in case they're not reentrant. */ 47219304Speter { 47319304Speter char const *p; 47419304Speter char buf[BUFSIZ], *b = buf; 47519304Speter for (p = s; *p; *b++ = *p++) 47619304Speter continue; 47719304Speter *b++ = ':'; 47819304Speter *b++ = ' '; 47919304Speter for (p = sname; *p; *b++ = *p++) 480254225Speter continue; 48119304Speter *b++ = '\n'; 48219304Speter VOID write(STDERR_FILENO, buf, b - buf); 48319304Speter } 48419304Speter} 48519304Speter#endif 48619304Speter 48719304Speterstatic signal_type catchsig P((int)); 48819304Speter#ifdef SA_SIGINFO 48919304Speter static signal_type catchsigaction P((int,siginfo_t*,void*)); 49019304Speter#endif 49119304Speter 49219304Speter static signal_type 49319304Spetercatchsig(s) 49419304Speter int s; 49519304Speter#ifdef SA_SIGINFO 49619304Speter{ 49719304Speter catchsigaction(s, (siginfo_t *)0, (void *)0); 49819304Speter} 49919304Speter static signal_type 50019304Spetercatchsigaction(s, i, c) 50119304Speter int s; 50219304Speter siginfo_t *i; 50319304Speter void *c; 50419304Speter#endif 50519304Speter{ 50619304Speter# if sig_zaps_handler 50719304Speter /* If a signal arrives before we reset the handler, we lose. */ 50819304Speter VOID signal(s, SIG_IGN); 50919304Speter# endif 51019304Speter 51119304Speter# ifdef SA_SIGINFO 51219304Speter if (!unsupported_SA_SIGINFO) 51319304Speter i = 0; 51419304Speter# endif 51519304Speter 51619304Speter if (holdlevel) { 51719304Speter heldsignal = s; 51819304Speter# ifdef SA_SIGINFO 51919304Speter if (i) { 52019304Speter bufsiginfo = *i; 52119304Speter heldsiginfo = &bufsiginfo; 52219304Speter } 52319304Speter# endif 52419304Speter return; 52519304Speter } 52619304Speter 52719304Speter ignoreints(); 52819304Speter setrid(); 52919304Speter if (!quietflag) { 53019304Speter /* Avoid calling sprintf etc., in case they're not reentrant. */ 53119304Speter char const *p; 53219304Speter char buf[BUFSIZ], *b = buf; 53319304Speter 534254225Speter if ( ! ( 535254225Speter# if has_mmap && large_memory && mmap_signal 53619304Speter /* Check whether this signal was planned. */ 53719304Speter s == mmap_signal && accessName 53819304Speter# else 53919304Speter 0 54019304Speter# endif 54119304Speter )) { 54219304Speter char const *nRCS = "\nRCS"; 54319304Speter# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal 54419304Speter if (s == mmap_signal && i && i->si_errno) { 54519304Speter errno = i->si_errno; 54619304Speter perror(nRCS++); 54719304Speter } 54819304Speter# endif 54919304Speter# if defined(SA_SIGINFO) && has_psiginfo 55019304Speter if (i) 55119304Speter psiginfo(i, nRCS); 55219304Speter else 55319304Speter psignal(s, nRCS); 55419304Speter# else 55519304Speter psignal(s, nRCS); 55619304Speter# endif 55719304Speter } 55819304Speter 55919304Speter for (p = "RCS: "; *p; *b++ = *p++) 56019304Speter continue; 56119304Speter# if has_mmap && large_memory && mmap_signal 56219304Speter if (s == mmap_signal) { 56319304Speter p = accessName; 564254225Speter if (!p) 565254225Speter p = "Was a file changed by some other process? "; 566254225Speter else { 56719304Speter char const *p1; 56819304Speter for (p1 = p; *p1; p1++) 56919304Speter continue; 57019304Speter VOID write(STDERR_FILENO, buf, b - buf); 57119304Speter VOID write(STDERR_FILENO, p, p1 - p); 57219304Speter b = buf; 57319304Speter p = ": Permission denied. "; 57419304Speter } 57519304Speter while (*p) 57619304Speter *b++ = *p++; 57719304Speter } 57819304Speter# endif 57919304Speter for (p = "Cleaning up.\n"; *p; *b++ = *p++) 58019304Speter continue; 58119304Speter VOID write(STDERR_FILENO, buf, b - buf); 58219304Speter } 58319304Speter exiterr(); 58419304Speter} 58519304Speter 58619304Speter void 58719304Speterignoreints() 58819304Speter{ 58919304Speter ++holdlevel; 59019304Speter} 59119304Speter 59219304Speter void 59319304Speterrestoreints() 59419304Speter{ 59519304Speter if (!--holdlevel && heldsignal) 59619304Speter# ifdef SA_SIGINFO 59719304Speter VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); 59819304Speter# else 59919304Speter VOID catchsig(heldsignal); 60019304Speter# endif 60119304Speter} 60219304Speter 60319304Speter 60419304Speterstatic void setup_catchsig P((int const*,int)); 60519304Speter 60619304Speter#if has_sigaction 60719304Speter 60819304Speter static void check_sig P((int)); 60919304Speter static void 61019304Speter check_sig(r) 61119304Speter int r; 61219304Speter { 61319304Speter if (r != 0) 614254225Speter efaterror("signal handling"); 615254225Speter } 61619304Speter 61719304Speter static void 61819304Speter setup_catchsig(sig, sigs) 61919304Speter int const *sig; 62019304Speter int sigs; 62119304Speter { 62219304Speter register int i, j; 62319304Speter struct sigaction act; 62419304Speter 62519304Speter for (i=sigs; 0<=--i; ) { 62619304Speter check_sig(sigaction(sig[i], (struct sigaction*)0, &act)); 62719304Speter if (act.sa_handler != SIG_IGN) { 62819304Speter act.sa_handler = catchsig; 62919304Speter# ifdef SA_SIGINFO 63019304Speter if (!unsupported_SA_SIGINFO) { 631254225Speter# if has_sa_sigaction 632254225Speter act.sa_sigaction = catchsigaction; 63319304Speter# else 63419304Speter act.sa_handler = catchsigaction; 63519304Speter# endif 63619304Speter act.sa_flags |= SA_SIGINFO; 63719304Speter } 63819304Speter# endif 63919304Speter for (j=sigs; 0<=--j; ) 64019304Speter check_sig(sigaddset(&act.sa_mask, sig[j])); 64119304Speter if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) { 64219304Speter# if defined(SA_SIGINFO) && defined(ENOTSUP) 64319304Speter if (errno == ENOTSUP && !unsupported_SA_SIGINFO) { 64419304Speter /* Turn off use of SA_SIGINFO and try again. */ 64519304Speter unsupported_SA_SIGINFO = 1; 64619304Speter i++; 64719304Speter continue; 64819304Speter } 64919304Speter# endif 65019304Speter check_sig(-1); 65119304Speter } 65219304Speter } 65319304Speter } 65419304Speter } 65519304Speter 65619304Speter#else 65719304Speter#if has_sigblock 65819304Speter 65919304Speter static void 66019304Speter setup_catchsig(sig, sigs) 661254225Speter int const *sig; 662254225Speter int sigs; 663254225Speter { 664254225Speter register int i; 66519304Speter int mask; 66619304Speter 66719304Speter mask = 0; 66819304Speter for (i=sigs; 0<=--i; ) 66919304Speter mask |= sigmask(sig[i]); 67019304Speter mask = sigblock(mask); 67119304Speter for (i=sigs; 0<=--i; ) 67219304Speter if ( 67319304Speter signal(sig[i], catchsig) == SIG_IGN && 67419304Speter signal(sig[i], SIG_IGN) != catchsig 67519304Speter ) 67619304Speter faterror("signal catcher failure"); 677254225Speter VOID sigsetmask(mask); 678254225Speter } 679254225Speter 680254225Speter#else 681254225Speter 68219304Speter static void 68319304Speter setup_catchsig(sig, sigs) 68419304Speter int const *sig; 68519304Speter int sigs; 68619304Speter { 68719304Speter register i; 68819304Speter 68919304Speter for (i=sigs; 0<=--i; ) 69019304Speter if ( 69119304Speter signal(sig[i], SIG_IGN) != SIG_IGN && 69219304Speter signal(sig[i], catchsig) != SIG_IGN 69319304Speter ) 69419304Speter faterror("signal catcher failure"); 69519304Speter } 69619304Speter 69719304Speter#endif 69819304Speter#endif 69919304Speter 70019304Speter 70119304Speterstatic int const regsigs[] = { 70219304Speter# ifdef SIGHUP 70319304Speter SIGHUP, 70419304Speter# endif 70519304Speter# ifdef SIGINT 70619304Speter SIGINT, 70719304Speter# endif 70819304Speter# ifdef SIGPIPE 70919304Speter SIGPIPE, 71019304Speter# endif 71119304Speter# ifdef SIGQUIT 71219304Speter SIGQUIT, 71319304Speter# endif 71419304Speter# ifdef SIGTERM 71519304Speter SIGTERM, 71619304Speter# endif 71719304Speter# ifdef SIGXCPU 71819304Speter SIGXCPU, 71919304Speter# endif 72019304Speter# ifdef SIGXFSZ 72119304Speter SIGXFSZ, 72219304Speter# endif 72319304Speter}; 72419304Speter 72519304Speter void 726254225Spetercatchints() 727254225Speter{ 728254225Speter static int catching_ints; 729254225Speter if (!catching_ints) { 730254225Speter catching_ints = true; 731254225Speter setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs))); 732254225Speter } 733254225Speter} 734254225Speter 735254225Speter#if has_mmap && large_memory && mmap_signal 736254225Speter 737254225Speter /* 738254225Speter * If you mmap an NFS file, and someone on another client removes the last 739254225Speter * link to that file, and you later reference an uncached part of that file, 740254225Speter * you'll get a SIGBUS or SIGSEGV (depending on the operating system). 741254225Speter * Catch the signal and report the problem to the user. 742254225Speter * Unfortunately, there's no portable way to differentiate between this 743254225Speter * problem and actual bugs in the program. 744254225Speter * This NFS problem is rare, thank goodness. 745254225Speter * 746254225Speter * This can also occur if someone truncates the file, even without NFS. 747254225Speter */ 748254225Speter 749254225Speter static int const mmapsigs[] = { mmap_signal }; 750254225Speter 751254225Speter void 752254225Speter catchmmapints() 753254225Speter { 754254225Speter static int catching_mmap_ints; 755254225Speter if (!catching_mmap_ints) { 756254225Speter catching_mmap_ints = true; 757254225Speter setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs))); 758254225Speter } 759254225Speter } 760254225Speter#endif 761254225Speter 762254225Speter#endif /* has_signal */ 763254225Speter 764254225Speter 765254225Speter void 766254225Speterfastcopy(inf,outf) 767254225Speter register RILE *inf; 768 FILE *outf; 769/* Function: copies the remainder of file inf to outf. 770 */ 771{ 772#if large_memory 773# if maps_memory 774 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); 775 inf->ptr = inf->lim; 776# else 777 for (;;) { 778 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf); 779 inf->ptr = inf->readlim; 780 if (inf->ptr == inf->lim) 781 break; 782 VOID Igetmore(inf); 783 } 784# endif 785#else 786 char buf[BUFSIZ*8]; 787 register fread_type rcount; 788 789 /*now read the rest of the file in blocks*/ 790 while (!feof(inf)) { 791 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) { 792 testIerror(inf); 793 return; 794 } 795 awrite(buf, (size_t)rcount, outf); 796 } 797#endif 798} 799 800#ifndef SSIZE_MAX 801 /* This does not work in #ifs, but it's good enough for us. */ 802 /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */ 803# define SSIZE_MAX ((unsigned)-1 >> 1) 804#endif 805 806 void 807awrite(buf, chars, f) 808 char const *buf; 809 size_t chars; 810 FILE *f; 811{ 812 /* Posix 1003.1-1990 ssize_t hack */ 813 while (SSIZE_MAX < chars) { 814 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) 815 Oerror(); 816 buf += SSIZE_MAX; 817 chars -= SSIZE_MAX; 818 } 819 820 if (Fwrite(buf, sizeof(*buf), chars, f) != chars) 821 Oerror(); 822} 823 824/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ 825 static int dupSafer P((int)); 826 static int 827dupSafer(fd) 828 int fd; 829{ 830# ifdef F_DUPFD 831 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); 832# else 833 int e, f, i, used = 0; 834 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) 835 used |= 1<<f; 836 e = errno; 837 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) 838 if (used & (1<<i)) 839 VOID close(i); 840 errno = e; 841 return f; 842# endif 843} 844 845/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */ 846 int 847fdSafer(fd) 848 int fd; 849{ 850 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { 851 int f = dupSafer(fd); 852 int e = errno; 853 VOID close(fd); 854 errno = e; 855 fd = f; 856 } 857 return fd; 858} 859 860/* Like fopen, except the result is never stdin, stdout, or stderr. */ 861 FILE * 862fopenSafer(filename, type) 863 char const *filename; 864 char const *type; 865{ 866 FILE *stream = fopen(filename, type); 867 if (stream) { 868 int fd = fileno(stream); 869 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { 870 int f = dupSafer(fd); 871 if (f < 0) { 872 int e = errno; 873 VOID fclose(stream); 874 errno = e; 875 return 0; 876 } 877 if (fclose(stream) != 0) { 878 int e = errno; 879 VOID close(f); 880 errno = e; 881 return 0; 882 } 883 stream = fdopen(f, type); 884 } 885 } 886 return stream; 887} 888 889 890#ifdef F_DUPFD 891# undef dup 892# define dup(fd) fcntl(fd, F_DUPFD, 0) 893#endif 894 895 896#if has_fork || has_spawn 897 898 static int movefd P((int,int)); 899 static int 900movefd(old, new) 901 int old, new; 902{ 903 if (old < 0 || old == new) 904 return old; 905# ifdef F_DUPFD 906 new = fcntl(old, F_DUPFD, new); 907# else 908 new = dup2(old, new); 909# endif 910 return close(old)==0 ? new : -1; 911} 912 913 static int fdreopen P((int,char const*,int)); 914 static int 915fdreopen(fd, file, flags) 916 int fd; 917 char const *file; 918 int flags; 919{ 920 int newfd; 921 VOID close(fd); 922 newfd = 923#if !open_can_creat 924 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) : 925#endif 926 open(file, flags, S_IRUSR|S_IWUSR); 927 return movefd(newfd, fd); 928} 929 930#if has_spawn 931 static void redirect P((int,int)); 932 static void 933redirect(old, new) 934 int old, new; 935/* 936* Move file descriptor OLD to NEW. 937* If OLD is -1, do nothing. 938* If OLD is -2, just close NEW. 939*/ 940{ 941 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0)) 942 efaterror("spawn I/O redirection"); 943} 944#endif 945 946 947#else /* !has_fork && !has_spawn */ 948 949 static void bufargcat P((struct buf*,int,char const*)); 950 static void 951bufargcat(b, c, s) 952 register struct buf *b; 953 int c; 954 register char const *s; 955/* Append to B a copy of C, plus a quoted copy of S. */ 956{ 957 register char *p; 958 register char const *t; 959 size_t bl, sl; 960 961 for (t=s, sl=0; *t; ) 962 sl += 3*(*t++=='\'') + 1; 963 bl = strlen(b->string); 964 bufrealloc(b, bl + sl + 4); 965 p = b->string + bl; 966 *p++ = c; 967 *p++ = '\''; 968 while (*s) { 969 if (*s == '\'') { 970 *p++ = '\''; 971 *p++ = '\\'; 972 *p++ = '\''; 973 } 974 *p++ = *s++; 975 } 976 *p++ = '\''; 977 *p = 0; 978} 979 980#endif 981 982#if !has_spawn && has_fork 983/* 984* Output the string S to stderr, without touching any I/O buffers. 985* This is useful if you are a child process, whose buffers are usually wrong. 986* Exit immediately if the write does not completely succeed. 987*/ 988static void write_stderr P((char const *)); 989 static void 990write_stderr(s) 991 char const *s; 992{ 993 size_t slen = strlen(s); 994 if (write(STDERR_FILENO, s, slen) != slen) 995 _exit(EXIT_TROUBLE); 996} 997#endif 998 999/* 1000* Run a command. 1001* infd, if not -1, is the input file descriptor. 1002* outname, if nonzero, is the name of the output file. 1003* args[1..] form the command to be run; args[0] might be modified. 1004*/ 1005 int 1006runv(infd, outname, args) 1007 int infd; 1008 char const *outname, **args; 1009{ 1010 int wstatus; 1011 1012#if bad_wait_if_SIGCHLD_ignored 1013 static int fixed_SIGCHLD; 1014 if (!fixed_SIGCHLD) { 1015 fixed_SIGCHLD = true; 1016# ifndef SIGCHLD 1017# define SIGCHLD SIGCLD 1018# endif 1019 VOID signal(SIGCHLD, SIG_DFL); 1020 } 1021#endif 1022 1023 oflush(); 1024 eflush(); 1025 { 1026#if has_spawn 1027 int in, out; 1028 char const *file; 1029 1030 in = -1; 1031 if (infd != -1 && infd != STDIN_FILENO) { 1032 if ((in = dup(STDIN_FILENO)) < 0) { 1033 if (errno != EBADF) 1034 efaterror("spawn input setup"); 1035 in = -2; 1036 } else { 1037# ifdef F_DUPFD 1038 if (close(STDIN_FILENO) != 0) 1039 efaterror("spawn input close"); 1040# endif 1041 } 1042 if ( 1043# ifdef F_DUPFD 1044 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO 1045# else 1046 dup2(infd, STDIN_FILENO) != STDIN_FILENO 1047# endif 1048 ) 1049 efaterror("spawn input redirection"); 1050 } 1051 1052 out = -1; 1053 if (outname) { 1054 if ((out = dup(STDOUT_FILENO)) < 0) { 1055 if (errno != EBADF) 1056 efaterror("spawn output setup"); 1057 out = -2; 1058 } 1059 if (fdreopen( 1060 STDOUT_FILENO, outname, 1061 O_CREAT | O_TRUNC | O_WRONLY 1062 ) < 0) 1063 efaterror(outname); 1064 } 1065 1066 wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); 1067# ifdef RCS_SHELL 1068 if (wstatus == -1 && errno == ENOEXEC) { 1069 args[0] = RCS_SHELL; 1070 wstatus = spawnv(0, args[0], (char**)args); 1071 } 1072# endif 1073 redirect(in, STDIN_FILENO); 1074 redirect(out, STDOUT_FILENO); 1075#else 1076#if has_fork 1077 pid_t pid; 1078 if (!(pid = vfork())) { 1079 char const *notfound; 1080 if (infd != -1 && infd != STDIN_FILENO && ( 1081# ifdef F_DUPFD 1082 (VOID close(STDIN_FILENO), 1083 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) 1084# else 1085 dup2(infd, STDIN_FILENO) != STDIN_FILENO 1086# endif 1087 )) { 1088 /* Avoid perror since it may misuse buffers. */ 1089 write_stderr(args[1]); 1090 write_stderr(": I/O redirection failed\n"); 1091 _exit(EXIT_TROUBLE); 1092 } 1093 1094 if (outname) 1095 if (fdreopen( 1096 STDOUT_FILENO, outname, 1097 O_CREAT | O_TRUNC | O_WRONLY 1098 ) < 0) { 1099 /* Avoid perror since it may misuse buffers. */ 1100 write_stderr(args[1]); 1101 write_stderr(": "); 1102 write_stderr(outname); 1103 write_stderr(": cannot create\n"); 1104 _exit(EXIT_TROUBLE); 1105 } 1106 VOID exec_RCS(args[1], (char**)(args + 1)); 1107 notfound = args[1]; 1108# ifdef RCS_SHELL 1109 if (errno == ENOEXEC) { 1110 args[0] = notfound = RCS_SHELL; 1111 VOID execv(args[0], (char**)args); 1112 } 1113# endif 1114 1115 /* Avoid perror since it may misuse buffers. */ 1116 write_stderr(notfound); 1117 write_stderr(": not found\n"); 1118 _exit(EXIT_TROUBLE); 1119 } 1120 if (pid < 0) 1121 efaterror("fork"); 1122# if has_waitpid 1123 if (waitpid(pid, &wstatus, 0) < 0) 1124 efaterror("waitpid"); 1125# else 1126 { 1127 pid_t w; 1128 do { 1129 if ((w = wait(&wstatus)) < 0) 1130 efaterror("wait"); 1131 } while (w != pid); 1132 } 1133# endif 1134#else 1135 static struct buf b; 1136 char const *p; 1137 1138 /* Use system(). On many hosts system() discards signals. Yuck! */ 1139 p = args + 1; 1140 bufscpy(&b, *p); 1141 while (*++p) 1142 bufargcat(&b, ' ', *p); 1143 if (infd != -1 && infd != STDIN_FILENO) { 1144 char redirection[32]; 1145 VOID sprintf(redirection, "<&%d", infd); 1146 bufscat(&b, redirection); 1147 } 1148 if (outname) 1149 bufargcat(&b, '>', outname); 1150 wstatus = system(b.string); 1151#endif 1152#endif 1153 } 1154 if (!WIFEXITED(wstatus)) { 1155 if (WIFSIGNALED(wstatus)) { 1156 psignal(WTERMSIG(wstatus), args[1]); 1157 fatcleanup(1); 1158 } 1159 faterror("%s failed for unknown reason", args[1]); 1160 } 1161 return WEXITSTATUS(wstatus); 1162} 1163 1164#define CARGSMAX 20 1165/* 1166* Run a command. 1167* infd, if not -1, is the input file descriptor. 1168* outname, if nonzero, is the name of the output file. 1169* The remaining arguments specify the command and its arguments. 1170*/ 1171 int 1172#if has_prototypes 1173run(int infd, char const *outname, ...) 1174#else 1175 /*VARARGS2*/ 1176run(infd, outname, va_alist) 1177 int infd; 1178 char const *outname; 1179 va_dcl 1180#endif 1181{ 1182 va_list ap; 1183 char const *rgargs[CARGSMAX]; 1184 register int i; 1185 vararg_start(ap, outname); 1186 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) 1187 if (CARGSMAX <= i) 1188 faterror("too many command arguments"); 1189 va_end(ap); 1190 return runv(infd, outname, rgargs); 1191} 1192 1193 1194int RCSversion; 1195 1196 void 1197setRCSversion(str) 1198 char const *str; 1199{ 1200 static int oldversion; 1201 1202 register char const *s = str + 2; 1203 1204 if (*s) { 1205 int v = VERSION_DEFAULT; 1206 1207 if (oldversion) 1208 redefined('V'); 1209 oldversion = true; 1210 v = 0; 1211 while (isdigit(*s)) 1212 v = 10*v + *s++ - '0'; 1213 if (*s) 1214 error("%s isn't a number", str); 1215 else if (v < VERSION_min || VERSION_max < v) 1216 error("%s out of range %d..%d", 1217 str, VERSION_min, VERSION_max 1218 ); 1219 1220 RCSversion = VERSION(v); 1221 } else { 1222 printf("RCS version %s\n", RCS_version_string); 1223 exit(0); 1224 } 1225} 1226 1227 int 1228getRCSINIT(argc, argv, newargv) 1229 int argc; 1230 char **argv, ***newargv; 1231{ 1232 register char *p, *q, **pp; 1233 size_t n; 1234 1235 if (!(q = cgetenv("RCSINIT"))) 1236 *newargv = argv; 1237 else { 1238 n = argc + 2; 1239 /* 1240 * Count spaces in RCSINIT to allocate a new arg vector. 1241 * This is an upper bound, but it's OK even if too large. 1242 */ 1243 for (p = q; ; ) { 1244 switch (*p++) { 1245 default: 1246 continue; 1247 1248 case ' ': 1249 case '\b': case '\f': case '\n': 1250 case '\r': case '\t': case '\v': 1251 n++; 1252 continue; 1253 1254 case '\0': 1255 break; 1256 } 1257 break; 1258 } 1259 *newargv = pp = tnalloc(char*, n); 1260 *pp++ = *argv++; /* copy program name */ 1261 for (p = q; ; ) { 1262 for (;;) { 1263 switch (*q) { 1264 case '\0': 1265 goto copyrest; 1266 1267 case ' ': 1268 case '\b': case '\f': case '\n': 1269 case '\r': case '\t': case '\v': 1270 q++; 1271 continue; 1272 } 1273 break; 1274 } 1275 *pp++ = p; 1276 ++argc; 1277 for (;;) { 1278 switch ((*p++ = *q++)) { 1279 case '\0': 1280 goto copyrest; 1281 1282 case '\\': 1283 if (!*q) 1284 goto copyrest; 1285 p[-1] = *q++; 1286 continue; 1287 1288 default: 1289 continue; 1290 1291 case ' ': 1292 case '\b': case '\f': case '\n': 1293 case '\r': case '\t': case '\v': 1294 break; 1295 } 1296 break; 1297 } 1298 p[-1] = '\0'; 1299 } 1300 copyrest: 1301 while ((*pp++ = *argv++)) 1302 continue; 1303 } 1304 return argc; 1305} 1306 1307 1308#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i 1309 1310#if has_getuid 1311 uid_t ruid() { cacheid(getuid()); } 1312#endif 1313#if has_setuid 1314 uid_t euid() { cacheid(geteuid()); } 1315#endif 1316 1317 1318#if has_setuid 1319 1320/* 1321 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), 1322 * because it lets us switch back and forth between arbitrary users. 1323 * If seteuid() doesn't work, we fall back on setuid(), 1324 * which works if saved setuid is supported, 1325 * unless the real or effective user is root. 1326 * This area is such a mess that we always check switches at runtime. 1327 */ 1328 1329 static void 1330#if has_prototypes 1331set_uid_to(uid_t u) 1332#else 1333 set_uid_to(u) uid_t u; 1334#endif 1335/* Become user u. */ 1336{ 1337 static int looping; 1338 1339 if (euid() == ruid()) 1340 return; 1341#if (has_fork||has_spawn) && DIFF_ABSOLUTE 1342# if has_setreuid 1343 if (setreuid(u==euid() ? ruid() : euid(), u) != 0) 1344 efaterror("setuid"); 1345# else 1346 if (seteuid(u) != 0) 1347 efaterror("setuid"); 1348# endif 1349#endif 1350 if (geteuid() != u) { 1351 if (looping) 1352 return; 1353 looping = true; 1354 faterror("root setuid not supported" + (u?5:0)); 1355 } 1356} 1357 1358static int stick_with_euid; 1359 1360 void 1361/* Ignore all calls to seteid() and setrid(). */ 1362nosetid() 1363{ 1364 stick_with_euid = true; 1365} 1366 1367 void 1368seteid() 1369/* Become effective user. */ 1370{ 1371 if (!stick_with_euid) 1372 set_uid_to(euid()); 1373} 1374 1375 void 1376setrid() 1377/* Become real user. */ 1378{ 1379 if (!stick_with_euid) 1380 set_uid_to(ruid()); 1381} 1382#endif 1383 1384 time_t 1385now() 1386{ 1387 static time_t t; 1388 if (!t && time(&t) == -1) 1389 efaterror("time"); 1390 return t; 1391} 1392