12311Sjkh/* Copyright 1988,1990,1993,1994 by Paul Vixie 22311Sjkh * All rights reserved 32311Sjkh * 42311Sjkh * Distribute freely, except: don't remove my name from the source or 52311Sjkh * documentation (don't take credit for my work), mark your changes (don't 62311Sjkh * get me blamed for your possible bugs), don't alter or remove this 72311Sjkh * notice. May be sold if buildable source is provided to buyer. No 82311Sjkh * warrantee of any kind, express or implied, is included with this 92311Sjkh * software; use at your own risk, responsibility for damages (if any) to 102311Sjkh * anyone resulting from the use of this software rests entirely with the 112311Sjkh * user. 122311Sjkh * 132311Sjkh * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 142311Sjkh * I'll try to keep a version up to date. I can be reached as follows: 152311Sjkh * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 162311Sjkh */ 172311Sjkh 182311Sjkh#if !defined(lint) && !defined(LINT) 1929452Scharnierstatic const char rcsid[] = 2050479Speter "$FreeBSD$"; 212311Sjkh#endif 222311Sjkh 232311Sjkh/* vix 26jan87 [RCS has the rest of the log] 242311Sjkh * vix 30dec86 [written] 252311Sjkh */ 262311Sjkh 272311Sjkh 282311Sjkh#include "cron.h" 292311Sjkh#if SYS_TIME_H 302311Sjkh# include <sys/time.h> 312311Sjkh#else 322311Sjkh# include <time.h> 332311Sjkh#endif 342311Sjkh#include <sys/file.h> 352311Sjkh#include <sys/stat.h> 3629452Scharnier#include <err.h> 372311Sjkh#include <errno.h> 382311Sjkh#include <string.h> 392311Sjkh#include <fcntl.h> 402311Sjkh#if defined(SYSLOG) 412311Sjkh# include <syslog.h> 422311Sjkh#endif 432311Sjkh 442311Sjkh 452311Sjkh#if defined(LOG_DAEMON) && !defined(LOG_CRON) 462311Sjkh#define LOG_CRON LOG_DAEMON 472311Sjkh#endif 482311Sjkh 492311Sjkh 502311Sjkhstatic int LogFD = ERR; 512311Sjkh 522311Sjkh 532311Sjkhint 542311Sjkhstrcmp_until(left, right, until) 552311Sjkh char *left; 562311Sjkh char *right; 572311Sjkh int until; 582311Sjkh{ 592311Sjkh register int diff; 602311Sjkh 612311Sjkh while (*left && *left != until && *left == *right) { 622311Sjkh left++; 632311Sjkh right++; 642311Sjkh } 652311Sjkh 662311Sjkh if ((*left=='\0' || *left == until) && 672311Sjkh (*right=='\0' || *right == until)) { 682311Sjkh diff = 0; 692311Sjkh } else { 702311Sjkh diff = *left - *right; 712311Sjkh } 722311Sjkh 732311Sjkh return diff; 742311Sjkh} 752311Sjkh 762311Sjkh 772311Sjkh/* strdtb(s) - delete trailing blanks in string 's' and return new length 782311Sjkh */ 792311Sjkhint 802311Sjkhstrdtb(s) 812311Sjkh char *s; 822311Sjkh{ 832311Sjkh char *x = s; 842311Sjkh 852311Sjkh /* scan forward to the null 862311Sjkh */ 872311Sjkh while (*x) 882311Sjkh x++; 892311Sjkh 902311Sjkh /* scan backward to either the first character before the string, 912311Sjkh * or the last non-blank in the string, whichever comes first. 922311Sjkh */ 932311Sjkh do {x--;} 942311Sjkh while (x >= s && isspace(*x)); 952311Sjkh 962311Sjkh /* one character beyond where we stopped above is where the null 972311Sjkh * goes. 982311Sjkh */ 992311Sjkh *++x = '\0'; 1002311Sjkh 1012311Sjkh /* the difference between the position of the null character and 1022311Sjkh * the position of the first character of the string is the length. 1032311Sjkh */ 1042311Sjkh return x - s; 1052311Sjkh} 1062311Sjkh 1072311Sjkh 1082311Sjkhint 1092311Sjkhset_debug_flags(flags) 1102311Sjkh char *flags; 1112311Sjkh{ 1122311Sjkh /* debug flags are of the form flag[,flag ...] 1132311Sjkh * 1142311Sjkh * if an error occurs, print a message to stdout and return FALSE. 1152311Sjkh * otherwise return TRUE after setting ERROR_FLAGS. 1162311Sjkh */ 1172311Sjkh 1182311Sjkh#if !DEBUGGING 1192311Sjkh 1202311Sjkh printf("this program was compiled without debugging enabled\n"); 1212311Sjkh return FALSE; 1222311Sjkh 1232311Sjkh#else /* DEBUGGING */ 1242311Sjkh 1252311Sjkh char *pc = flags; 1262311Sjkh 1272311Sjkh DebugFlags = 0; 1282311Sjkh 1292311Sjkh while (*pc) { 1302311Sjkh char **test; 1312311Sjkh int mask; 1322311Sjkh 1332311Sjkh /* try to find debug flag name in our list. 1342311Sjkh */ 1352311Sjkh for ( test = DebugFlagNames, mask = 1; 1362311Sjkh *test && strcmp_until(*test, pc, ','); 1372311Sjkh test++, mask <<= 1 1382311Sjkh ) 1392311Sjkh ; 1402311Sjkh 1412311Sjkh if (!*test) { 1422311Sjkh fprintf(stderr, 1432311Sjkh "unrecognized debug flag <%s> <%s>\n", 1442311Sjkh flags, pc); 1452311Sjkh return FALSE; 1462311Sjkh } 1472311Sjkh 1482311Sjkh DebugFlags |= mask; 1492311Sjkh 1502311Sjkh /* skip to the next flag 1512311Sjkh */ 1522311Sjkh while (*pc && *pc != ',') 1532311Sjkh pc++; 1542311Sjkh if (*pc == ',') 1552311Sjkh pc++; 1562311Sjkh } 1572311Sjkh 1582311Sjkh if (DebugFlags) { 1592311Sjkh int flag; 1602311Sjkh 1612311Sjkh fprintf(stderr, "debug flags enabled:"); 1622311Sjkh 1632311Sjkh for (flag = 0; DebugFlagNames[flag]; flag++) 1642311Sjkh if (DebugFlags & (1 << flag)) 1652311Sjkh fprintf(stderr, " %s", DebugFlagNames[flag]); 1662311Sjkh fprintf(stderr, "\n"); 1672311Sjkh } 1682311Sjkh 1692311Sjkh return TRUE; 1702311Sjkh 1712311Sjkh#endif /* DEBUGGING */ 1722311Sjkh} 1732311Sjkh 1742311Sjkh 1752311Sjkhvoid 1762311Sjkhset_cron_uid() 1772311Sjkh{ 1782311Sjkh#if defined(BSD) || defined(POSIX) 17929452Scharnier if (seteuid(ROOT_UID) < OK) 18029452Scharnier err(ERROR_EXIT, "seteuid"); 1812311Sjkh#else 18229452Scharnier if (setuid(ROOT_UID) < OK) 18329452Scharnier err(ERROR_EXIT, "setuid"); 1842311Sjkh#endif 1852311Sjkh} 1862311Sjkh 1872311Sjkh 1882311Sjkhvoid 1892311Sjkhset_cron_cwd() 1902311Sjkh{ 1912311Sjkh struct stat sb; 1922311Sjkh 1932311Sjkh /* first check for CRONDIR ("/var/cron" or some such) 1942311Sjkh */ 1952311Sjkh if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { 19629452Scharnier warn("%s", CRONDIR); 1972311Sjkh if (OK == mkdir(CRONDIR, 0700)) { 19829452Scharnier warnx("%s: created", CRONDIR); 1992311Sjkh stat(CRONDIR, &sb); 2002311Sjkh } else { 20129452Scharnier err(ERROR_EXIT, "%s: mkdir", CRONDIR); 2022311Sjkh } 2032311Sjkh } 20429452Scharnier if (!(sb.st_mode & S_IFDIR)) 20529452Scharnier err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR); 20629452Scharnier if (chdir(CRONDIR) < OK) 20729452Scharnier err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR); 2082311Sjkh 2092311Sjkh /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) 2102311Sjkh */ 2112311Sjkh if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { 21229452Scharnier warn("%s", SPOOL_DIR); 2132311Sjkh if (OK == mkdir(SPOOL_DIR, 0700)) { 21429452Scharnier warnx("%s: created", SPOOL_DIR); 2152311Sjkh stat(SPOOL_DIR, &sb); 2162311Sjkh } else { 21729452Scharnier err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR); 2182311Sjkh } 2192311Sjkh } 22029452Scharnier if (!(sb.st_mode & S_IFDIR)) 22129452Scharnier err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR); 2222311Sjkh} 2232311Sjkh 2242311Sjkh 2252311Sjkh/* get_char(file) : like getc() but increment LineNumber on newlines 2262311Sjkh */ 2272311Sjkhint 2282311Sjkhget_char(file) 2292311Sjkh FILE *file; 2302311Sjkh{ 2312311Sjkh int ch; 2322311Sjkh 2332311Sjkh ch = getc(file); 2342311Sjkh if (ch == '\n') 2352311Sjkh Set_LineNum(LineNumber + 1) 2362311Sjkh return ch; 2372311Sjkh} 2382311Sjkh 2392311Sjkh 2402311Sjkh/* unget_char(ch, file) : like ungetc but do LineNumber processing 2412311Sjkh */ 2422311Sjkhvoid 2432311Sjkhunget_char(ch, file) 2442311Sjkh int ch; 2452311Sjkh FILE *file; 2462311Sjkh{ 2472311Sjkh ungetc(ch, file); 2482311Sjkh if (ch == '\n') 2492311Sjkh Set_LineNum(LineNumber - 1) 2502311Sjkh} 2512311Sjkh 2522311Sjkh 2532311Sjkh/* get_string(str, max, file, termstr) : like fgets() but 2542311Sjkh * (1) has terminator string which should include \n 2552311Sjkh * (2) will always leave room for the null 2562311Sjkh * (3) uses get_char() so LineNumber will be accurate 2572311Sjkh * (4) returns EOF or terminating character, whichever 2582311Sjkh */ 2592311Sjkhint 2602311Sjkhget_string(string, size, file, terms) 2612311Sjkh char *string; 2622311Sjkh int size; 2632311Sjkh FILE *file; 2642311Sjkh char *terms; 2652311Sjkh{ 2662311Sjkh int ch; 2672311Sjkh 2682311Sjkh while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 2692311Sjkh if (size > 1) { 2702311Sjkh *string++ = (char) ch; 2712311Sjkh size--; 2722311Sjkh } 2732311Sjkh } 2742311Sjkh 2752311Sjkh if (size > 0) 2762311Sjkh *string = '\0'; 2772311Sjkh 2782311Sjkh return ch; 2792311Sjkh} 2802311Sjkh 2812311Sjkh 2822311Sjkh/* skip_comments(file) : read past comment (if any) 2832311Sjkh */ 2842311Sjkhvoid 2852311Sjkhskip_comments(file) 2862311Sjkh FILE *file; 2872311Sjkh{ 2882311Sjkh int ch; 2892311Sjkh 2902311Sjkh while (EOF != (ch = get_char(file))) { 2912311Sjkh /* ch is now the first character of a line. 2922311Sjkh */ 2932311Sjkh 2942311Sjkh while (ch == ' ' || ch == '\t') 2952311Sjkh ch = get_char(file); 2962311Sjkh 2972311Sjkh if (ch == EOF) 2982311Sjkh break; 2992311Sjkh 3002311Sjkh /* ch is now the first non-blank character of a line. 3012311Sjkh */ 3022311Sjkh 3032311Sjkh if (ch != '\n' && ch != '#') 3042311Sjkh break; 3052311Sjkh 3062311Sjkh /* ch must be a newline or comment as first non-blank 3072311Sjkh * character on a line. 3082311Sjkh */ 3092311Sjkh 3102311Sjkh while (ch != '\n' && ch != EOF) 3112311Sjkh ch = get_char(file); 3122311Sjkh 3132311Sjkh /* ch is now the newline of a line which we're going to 3142311Sjkh * ignore. 3152311Sjkh */ 3162311Sjkh } 3172311Sjkh if (ch != EOF) 3182311Sjkh unget_char(ch, file); 3192311Sjkh} 3202311Sjkh 3212311Sjkh 3222311Sjkh/* int in_file(char *string, FILE *file) 3232311Sjkh * return TRUE if one of the lines in file matches string exactly, 3242311Sjkh * FALSE otherwise. 3252311Sjkh */ 3262311Sjkhstatic int 327184809Smatteoin_file(char *string, FILE *file) 3282311Sjkh{ 3292311Sjkh char line[MAX_TEMPSTR]; 3302311Sjkh 3312311Sjkh rewind(file); 3322311Sjkh while (fgets(line, MAX_TEMPSTR, file)) { 3332311Sjkh if (line[0] != '\0') 33494389Sdwmalone if (line[strlen(line)-1] == '\n') 33594389Sdwmalone line[strlen(line)-1] = '\0'; 3362311Sjkh if (0 == strcmp(line, string)) 3372311Sjkh return TRUE; 3382311Sjkh } 3392311Sjkh return FALSE; 3402311Sjkh} 3412311Sjkh 3422311Sjkh 3432311Sjkh/* int allowed(char *username) 3442311Sjkh * returns TRUE if (ALLOW_FILE exists and user is listed) 3452311Sjkh * or (DENY_FILE exists and user is NOT listed) 3462311Sjkh * or (neither file exists but user=="root" so it's okay) 3472311Sjkh */ 3482311Sjkhint 3492311Sjkhallowed(username) 3502311Sjkh char *username; 3512311Sjkh{ 352101293Stjr FILE *allow, *deny; 353101293Stjr int isallowed; 3542311Sjkh 355101293Stjr isallowed = FALSE; 356101293Stjr 357141571Sstefanf deny = NULL; 3582311Sjkh#if defined(ALLOW_FILE) && defined(DENY_FILE) 359101293Stjr if ((allow = fopen(ALLOW_FILE, "r")) == NULL && errno != ENOENT) 360101293Stjr goto out; 361101293Stjr if ((deny = fopen(DENY_FILE, "r")) == NULL && errno != ENOENT) 362101293Stjr goto out; 363101293Stjr Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) 3642311Sjkh#else 365101293Stjr allow = NULL; 3662311Sjkh#endif 3672311Sjkh 3682311Sjkh if (allow) 369101293Stjr isallowed = in_file(username, allow); 370101293Stjr else if (deny) 371101293Stjr isallowed = !in_file(username, deny); 372101293Stjr else { 3732311Sjkh#if defined(ALLOW_ONLY_ROOT) 374101293Stjr isallowed = (strcmp(username, ROOT_USER) == 0); 3752311Sjkh#else 376101293Stjr isallowed = TRUE; 3772311Sjkh#endif 378101293Stjr } 379101293Stjrout: if (allow) 380101293Stjr fclose(allow); 381101293Stjr if (deny) 382101293Stjr fclose(deny); 383101293Stjr return (isallowed); 3842311Sjkh} 3852311Sjkh 3862311Sjkh 3872311Sjkhvoid 3882311Sjkhlog_it(username, xpid, event, detail) 3892311Sjkh char *username; 3902311Sjkh int xpid; 3912311Sjkh char *event; 3922311Sjkh char *detail; 3932311Sjkh{ 394241125Spluknet#if defined(LOG_FILE) || DEBUGGING 3952311Sjkh PID_T pid = xpid; 396241125Spluknet#endif 3972311Sjkh#if defined(LOG_FILE) 3982311Sjkh char *msg; 3992311Sjkh TIME_T now = time((TIME_T) 0); 4002311Sjkh register struct tm *t = localtime(&now); 4012311Sjkh#endif /*LOG_FILE*/ 4022311Sjkh 4032311Sjkh#if defined(SYSLOG) 4042311Sjkh static int syslog_open = 0; 4052311Sjkh#endif 4062311Sjkh 4072311Sjkh#if defined(LOG_FILE) 4082311Sjkh /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 4092311Sjkh */ 4102311Sjkh msg = malloc(strlen(username) 4112311Sjkh + strlen(event) 4122311Sjkh + strlen(detail) 4132311Sjkh + MAX_TEMPSTR); 4142311Sjkh 41560825Sghelmer if (msg == NULL) 41660825Sghelmer warnx("failed to allocate memory for log message"); 41760825Sghelmer else { 4182311Sjkh if (LogFD < OK) { 41960825Sghelmer LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 42060825Sghelmer if (LogFD < OK) { 42160825Sghelmer warn("can't open log file %s", LOG_FILE); 42260825Sghelmer } else { 42360825Sghelmer (void) fcntl(LogFD, F_SETFD, 1); 42460825Sghelmer } 4252311Sjkh } 4262311Sjkh 42760825Sghelmer /* we have to sprintf() it because fprintf() doesn't always 42860825Sghelmer * write everything out in one chunk and this has to be 42960825Sghelmer * atomically appended to the log file. 43060825Sghelmer */ 43160825Sghelmer sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 43260825Sghelmer username, 43360825Sghelmer t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, 43460825Sghelmer t->tm_sec, pid, event, detail); 4352311Sjkh 43660825Sghelmer /* we have to run strlen() because sprintf() returns (char*) 43760825Sghelmer * on old BSD. 43860825Sghelmer */ 43960825Sghelmer if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 44060825Sghelmer if (LogFD >= OK) 44160825Sghelmer warn("%s", LOG_FILE); 44260825Sghelmer warnx("can't write to log file"); 44360825Sghelmer write(STDERR, msg, strlen(msg)); 44460825Sghelmer } 44560825Sghelmer 44660825Sghelmer free(msg); 4472311Sjkh } 4482311Sjkh#endif /*LOG_FILE*/ 4492311Sjkh 4502311Sjkh#if defined(SYSLOG) 4512311Sjkh if (!syslog_open) { 4522311Sjkh /* we don't use LOG_PID since the pid passed to us by 4532311Sjkh * our client may not be our own. therefore we want to 4542311Sjkh * print the pid ourselves. 4552311Sjkh */ 4562311Sjkh# ifdef LOG_DAEMON 4572311Sjkh openlog(ProgramName, LOG_PID, LOG_CRON); 4582311Sjkh# else 4592311Sjkh openlog(ProgramName, LOG_PID); 4602311Sjkh# endif 4612311Sjkh syslog_open = TRUE; /* assume openlog success */ 4622311Sjkh } 4632311Sjkh 4642311Sjkh syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); 4652311Sjkh 4662311Sjkh#endif /*SYSLOG*/ 4672311Sjkh 4682311Sjkh#if DEBUGGING 4692311Sjkh if (DebugFlags) { 4702311Sjkh fprintf(stderr, "log_it: (%s %d) %s (%s)\n", 4712311Sjkh username, pid, event, detail); 4722311Sjkh } 4732311Sjkh#endif 4742311Sjkh} 4752311Sjkh 4762311Sjkh 4772311Sjkhvoid 4782311Sjkhlog_close() { 4792311Sjkh if (LogFD != ERR) { 4802311Sjkh close(LogFD); 4812311Sjkh LogFD = ERR; 4822311Sjkh } 4832311Sjkh} 4842311Sjkh 4852311Sjkh 4862311Sjkh/* two warnings: 4872311Sjkh * (1) this routine is fairly slow 4882311Sjkh * (2) it returns a pointer to static storage 4892311Sjkh */ 4902311Sjkhchar * 4912311Sjkhfirst_word(s, t) 4922311Sjkh register char *s; /* string we want the first word of */ 4932311Sjkh register char *t; /* terminators, implicitly including \0 */ 4942311Sjkh{ 4952311Sjkh static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ 4962311Sjkh static int retsel = 0; 4972311Sjkh register char *rb, *rp; 4982311Sjkh 4992311Sjkh /* select a return buffer */ 5002311Sjkh retsel = 1-retsel; 5012311Sjkh rb = &retbuf[retsel][0]; 5022311Sjkh rp = rb; 5032311Sjkh 5042311Sjkh /* skip any leading terminators */ 5052311Sjkh while (*s && (NULL != strchr(t, *s))) { 5062311Sjkh s++; 5072311Sjkh } 5082311Sjkh 5092311Sjkh /* copy until next terminator or full buffer */ 5102311Sjkh while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { 5112311Sjkh *rp++ = *s++; 5122311Sjkh } 5132311Sjkh 5142311Sjkh /* finish the return-string and return it */ 5152311Sjkh *rp = '\0'; 5162311Sjkh return rb; 5172311Sjkh} 5182311Sjkh 5192311Sjkh 5202311Sjkh/* warning: 5212311Sjkh * heavily ascii-dependent. 5222311Sjkh */ 523185043Smatteostatic void 524184809Smatteomkprint(register char *dst, register unsigned char *src, register int len) 5252311Sjkh{ 5262311Sjkh while (len-- > 0) 5272311Sjkh { 5282311Sjkh register unsigned char ch = *src++; 5292311Sjkh 5302311Sjkh if (ch < ' ') { /* control character */ 5312311Sjkh *dst++ = '^'; 5322311Sjkh *dst++ = ch + '@'; 5332311Sjkh } else if (ch < 0177) { /* printable */ 5342311Sjkh *dst++ = ch; 5352311Sjkh } else if (ch == 0177) { /* delete/rubout */ 5362311Sjkh *dst++ = '^'; 5372311Sjkh *dst++ = '?'; 5382311Sjkh } else { /* parity character */ 5392311Sjkh sprintf(dst, "\\%03o", ch); 5402311Sjkh dst += 4; 5412311Sjkh } 5422311Sjkh } 5432311Sjkh *dst = '\0'; 5442311Sjkh} 5452311Sjkh 5462311Sjkh 5472311Sjkh/* warning: 5482311Sjkh * returns a pointer to malloc'd storage, you must call free yourself. 5492311Sjkh */ 5502311Sjkhchar * 5512311Sjkhmkprints(src, len) 5522311Sjkh register unsigned char *src; 5532311Sjkh register unsigned int len; 5542311Sjkh{ 5552311Sjkh register char *dst = malloc(len*4 + 1); 5562311Sjkh 55760825Sghelmer if (dst != NULL) 55860825Sghelmer mkprint(dst, src, len); 5592311Sjkh 5602311Sjkh return dst; 5612311Sjkh} 5622311Sjkh 5632311Sjkh 5642311Sjkh#ifdef MAIL_DATE 5652311Sjkh/* Sat, 27 Feb 93 11:44:51 CST 5662311Sjkh * 123456789012345678901234567 5672311Sjkh */ 5682311Sjkhchar * 5692311Sjkharpadate(clock) 5702311Sjkh time_t *clock; 5712311Sjkh{ 5722311Sjkh time_t t = clock ?*clock :time(0L); 5732311Sjkh struct tm *tm = localtime(&t); 57442711Sdanny static char ret[32]; /* zone name might be >3 chars */ 5758857Srgrimes 57642711Sdanny if (tm->tm_year >= 100) 57742711Sdanny tm->tm_year += 1900; 57842711Sdanny 57942711Sdanny (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s", 5802311Sjkh DowNames[tm->tm_wday], 5812311Sjkh tm->tm_mday, 5822311Sjkh MonthNames[tm->tm_mon], 5832311Sjkh tm->tm_year, 5842311Sjkh tm->tm_hour, 5852311Sjkh tm->tm_min, 5862311Sjkh tm->tm_sec, 5872311Sjkh TZONE(*tm)); 5882311Sjkh return ret; 5892311Sjkh} 5902311Sjkh#endif /*MAIL_DATE*/ 5912311Sjkh 5922311Sjkh 5938164Sache#ifdef HAVE_SAVED_UIDS 5942311Sjkhstatic int save_euid; 5952311Sjkhint swap_uids() { save_euid = geteuid(); return seteuid(getuid()); } 5962311Sjkhint swap_uids_back() { return seteuid(save_euid); } 5972311Sjkh#else /*HAVE_SAVED_UIDS*/ 5982311Sjkhint swap_uids() { return setreuid(geteuid(), getuid()); } 5992311Sjkhint swap_uids_back() { return swap_uids(); } 6002311Sjkh#endif /*HAVE_SAVED_UIDS*/ 601