This is global.c in view mode; [Download] [Up]
/* * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. * * Global declarations and auxiliary functions. */ #include <signal.h> #include <errno.h> #include <pwd.h> #include "config.h" #include "patchlevel.h" export char *home_directory; export char *nn_directory; /* ~/.nn */ export char *news_directory; /* /usr/spool/news */ export char *news_lib_directory; /* /usr/lib/news */ export char *lib_directory; /* /usr/local/lib/nn */ export char *master_directory; /* = lib */ export char *help_directory; /* = lib/help */ export char *bin_directory = BIN_DIRECTORY; export char *db_directory; /* /usr/spool/nn or NEWS_DIR/.nn */ export char *db_data_directory; /* ..../DATA or undefined */ export int db_data_subdirs = 0; /* set if DATA/[0-9]/ exist */ export char *news_active; /* NLIB/active or DB/ACTIVE */ export char *pager; export char *log_file; /* = lib/Log */ export char *log_entry_filter = NULL; export char *temp_file; #ifndef TMP_DIRECTORY #define TMP_DIRECTORY "/usr/tmp" #endif export char *tmp_directory = TMP_DIRECTORY; export char version_id[32]; #ifdef NNTP export int use_nntp = 0; /* bool: t iff we use nntp */ #endif export unsigned short user_eid; export unsigned short user_id, group_id; export int process_id; export int who_am_i; export int dont_write_console = 0; export int mail_errors_mode = 2; #ifdef HAVE_MULTIGROUP #ifndef NGROUPS #include <sys/param.h> #endif #ifndef GIDSET_TYPE #define GIDSET_TYPE int #endif static int ngroups; static GIDSET_TYPE gidset[NGROUPS]; static in_grplist(gid) GIDSET_TYPE gid; { int n; if (gid == group_id) return 1; for (n = 0; n < ngroups; ++n) if (gidset[n] == gid) return 1; return 0; } #define group_access(gpid) in_grplist((GIDSET_TYPE)(gpid)) #else #define group_access(gid) ((gid) == group_id) #endif /* signal handler interface */ export int s_hangup = 0; /* hangup signal */ export int s_keyboard = 0; /* keyboard interrupt */ export int s_pipe = 0; /* broken pipe */ export int s_redraw = 0; /* redraw signal (if job control) */ #ifdef RESIZING export int s_resized = 0; /* screen resized */ #endif #ifdef FAKE_INTERRUPT #include <setjmp.h> export jmp_buf fake_keyb_sig; export int arm_fake_keyb_sig = 0; export char fake_keyb_siglist[NSIG]; #endif sig_type catch_hangup(n) { s_hangup = 1; signal(n, SIG_IGN); #ifdef FAKE_INTERRUPT if (fake_keyb_siglist[n] && arm_fake_keyb_sig) longjmp(fake_keyb_sig, 1); #endif } static sig_type catch_keyboard(n) { #ifdef RESET_SIGNAL_WHEN_CAUGHT signal(n, catch_keyboard); #endif #ifdef FAKE_INTERRUPT if (fake_keyb_siglist[n] && arm_fake_keyb_sig) longjmp(fake_keyb_sig, 1); #endif s_keyboard++; } static sig_type catch_pipe(n) { s_pipe++; #ifdef RESET_SIGNAL_WHEN_CAUGHT signal(n, catch_pipe); #endif #ifdef FAKE_INTERRUPT if (fake_keyb_siglist[n] && arm_fake_keyb_sig) longjmp(fake_keyb_sig, 1); #endif } #ifdef HAVE_JOBCONTROL static sig_type catch_suspend(n) { s_redraw++; #ifdef RESET_SIGNAL_WHEN_CAUGHT signal(n, catch_suspend); #endif suspend_nn(); #ifdef FAKE_INTERRUPT if (fake_keyb_siglist[n] && arm_fake_keyb_sig) { #ifdef RESIZING s_resized++; #endif longjmp(fake_keyb_sig, 1); } #endif } #endif init_global() { unsigned short getuid(), getgid(); int getpid(); int suspend_nn(); int i; #ifdef FAKE_INTERRUPT for (i = 0; i < NSIG; i++) fake_keyb_siglist[i] = i == 2 ? 1 : 0; #endif if (who_am_i != I_AM_NN) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } signal(SIGTERM, catch_hangup); signal(SIGHUP, catch_hangup); signal(SIGPIPE, catch_pipe); signal(SIGALRM, SIG_IGN); #ifdef SIGPWR signal(SIGPWR, catch_hangup); #endif #ifdef CONFIG_NUM_IN_VERSION sprintf(version_id, "%s.%d #%d", RELEASE, PATCHLEVEL, #include "update.h" ); #else sprintf(version_id, "%s.%d", RELEASE, PATCHLEVEL); #endif user_id = getuid(); #ifdef HAVE_MULTIGROUP ngroups = getgroups(NGROUPS, gidset); /* Get users's group set */ #endif group_id = getegid(); user_eid = geteuid(); process_id = getpid(); #ifdef CLIENT_DIRECTORY lib_directory = CLIENT_DIRECTORY; #else lib_directory = LIB_DIRECTORY; #endif #ifdef NEWS_DIRECTORY news_directory = NEWS_DIRECTORY; #else news_directory = "/usr/spool/news"; #endif #ifdef DB_DIRECTORY db_directory = DB_DIRECTORY; #else db_directory = mk_file_name(news_directory, ".nn"); #endif #ifdef ACCOUNTING if (who_am_i == I_AM_ACCT) return 0; #endif #ifdef DB_DATA_DIRECTORY db_data_directory = DB_DATA_DIRECTORY; #else #ifdef DB_DIRECTORY db_data_directory = mk_file_name(db_directory, "DATA"); #else db_data_directory = NULL; #endif #endif #ifndef DB_LONG_NAMES if (db_data_directory != NULL) db_data_subdirs = file_exist(relative(db_data_directory, "0"), "dx"); #endif #ifdef NEWS_LIB_DIRECTORY news_lib_directory = NEWS_LIB_DIRECTORY; #else news_lib_directory = "/usr/lib/news"; #endif /* this may later be changed by nntp_check */ news_active = mk_file_name(news_lib_directory, "active"); #ifdef MASTER_DIRECTORY master_directory = MASTER_DIRECTORY; #else master_directory = LIB_DIRECTORY; #endif #ifdef HELP_DIRECTORY help_directory = HELP_DIRECTORY; #else help_directory = mk_file_name(lib_directory, "help"); #endif #ifdef LOG_FILE log_file = LOG_FILE; #else log_file = mk_file_name(LIB_DIRECTORY, "Log"); #endif if (who_am_i == I_AM_MASTER || who_am_i == I_AM_SPEW) return 0; signal(SIGINT, catch_keyboard); signal(SIGQUIT, catch_keyboard); #ifdef HAVE_JOBCONTROL signal(SIGTSTP, catch_suspend); #endif if ((home_directory = getenv("HOME")) == NULL) user_error("No HOME environment variable"); if ((pager = getenv("PAGER")) == NULL) pager = DEFAULT_PAGER; nn_directory = mk_file_name(home_directory, ".nn"); if (who_am_i != I_AM_ADMIN && !file_exist(nn_directory, "drwx")) { if (who_am_i != I_AM_NN) return -1; if (mkdir(nn_directory, 0755) < 0) user_error("Cannot create %s directory", nn_directory); return 1; } return 0; } new_temp_file() { static char buf[FILENAME]; static char *temp_dir = NULL; if (temp_dir == NULL) if ((temp_dir = getenv("TMPDIR")) == NULL) temp_dir = tmp_directory; /* just to make test above false */ else tmp_directory = temp_dir; sprintf(buf, "%s/nn.XXXXXX", tmp_directory); mktemp(buf); temp_file = buf; } FILE *open_file(name, mode) char *name; int mode; { FILE *f; int fd; extern int errno; if ((mode & DONT_CREATE) && !file_exist(name, (char *)NULL)) return NULL; switch (mode & 0x0f) { case OPEN_READ: f = fopen(name, "r"); break; case OPEN_UPDATE: /* f = fopen(name, "r+"); -- not reliable on many systems (sigh) */ if ((fd = open(name, O_WRONLY)) >= 0) { if ((f = fdopen(fd, "w")) != NULL) return f; close(fd); } /* fall thru */ case OPEN_CREATE: f = fopen(name, "w"); break; case OPEN_APPEND: f = fopen(name, "a"); break; case OPEN_CREATE_RW: f = fopen(name, "w+"); /* not safe on all systems -- beware */ break; default: sys_error("Illegal mode: open_file(%s, 0x%x)", name, mode); } if (f) { if (mode & OPEN_UNLINK) unlink(name); return f; } if ((mode & MUST_EXIST) == 0) return NULL; sys_error("Cannot open file %s, mode=0x%x, errno=%d", name, mode, errno); return NULL; } FILE *open_file_search_path(name, mode) char *name; int mode; { FILE *f; if (name == NULL) return NULL; if (*name == '/') return open_file(name, mode); f = NULL; if (!use_nntp) f = open_file(relative(news_lib_directory, name), OPEN_READ); if (f == NULL) f= open_file(relative(lib_directory, name), OPEN_READ); if (f == NULL) f = open_file(relative(db_directory, name), OPEN_READ); return f; } fgets_multi(buf, size, f) char *buf; int size; register FILE *f; { register char *s = buf; register int c, n = size; while (--n > 0) { c = getc(f); if (c == '\\') { if ((c = getc(f)) == NL) continue; *s++ = '\\'; if (--n < 0) break; } if (c == EOF) { *s = NUL; return s != buf; } if (c == NL) { *s = NUL; return 1; } *s++ = c; } buf[30] = NUL; sys_error("Line too long \"%s...\" (max %d)", buf, size); } /* * relative -- concat directory name and file name */ char *relative(dir, name) char *dir, *name; { static char concat_path[FILENAME]; sprintf(concat_path, "%s/%s", dir, name); return concat_path; } char *mk_file_name(dir, name) char *dir, *name; { char *buf; buf = newstr(strlen(dir) + strlen(name) + 2); sprintf(buf, "%s/%s", dir, name); return buf; } char *home_relative(dir) char *dir; { if (dir) { if (*dir == '/') return copy_str(dir); else { if (*dir == '~' && *++dir == '/') dir++; return mk_file_name(home_directory, dir); } } return NULL; } char *substchr(str, c1, c2) char *str, c1, c2; { char *p; if ((p = strchr(str, c1)) != NULL) *p = c2; return p; } char *copy_str(str) char *str; { char *new; new = newstr(strlen(str) + 1); if (new) strcpy(new, str); return new; } time_t m_time(f) FILE *f; { struct stat st; if (fstat(fileno(f), &st) < 0) return 0; return st.st_mtime; } time_t file_exist(name, mode) char *name; char *mode; { static struct stat statb; extern int errno; int mask; if (name != NULL && stat(name, &statb)) return 0; if (mode == NULL) return statb.st_mtime; if (statb.st_uid == user_eid) mask = 0700; else if (group_access(statb.st_gid)) mask = 0070; else mask = 0007; while (*mode) { switch (*mode++) { case 'd': if ((statb.st_mode & S_IFMT) == S_IFDIR) continue; errno = ENOTDIR; return 0; case 'f': if ((statb.st_mode & S_IFMT) == S_IFREG) continue; if ((statb.st_mode & S_IFMT) == 0000000) continue; if ((statb.st_mode & S_IFMT) == S_IFDIR) { errno = EISDIR; return 0; } break; case 'r': if (statb.st_mode & mask & 0444) continue; break; case 'w': if (statb.st_mode & mask & 0222) continue; break; case 'x': if (statb.st_mode & mask & 0111) continue; break; } errno = EACCES; return 0; } /* all modes are ok */ return statb.st_mtime; } /* * copy_file: copy (or append) src file to dest file. * * Returns number of characters copied or an error code: * -1: source file not found * -2: cannot create destination * -3: write error */ int32 copy_file(src, dest, append) char *src, *dest; int append; { register FILE *s, *d; register int32 n = 0; register int c; s = open_file(src, OPEN_READ); if (s == NULL) return -1; d = open_file(dest, append ? OPEN_APPEND : OPEN_CREATE); if (d == NULL) { fclose(s); return -2; } n = 0; while ((c = getc(s)) != EOF) { putc(c, d); n++; } fclose(s); if (fclose(d) == EOF) { if (!append) unlink(dest); return -3; } return n; } /* * move_file: move old file to new file, linking if possible. * * The third arg determines what is acceptable if the old file cannot be * removed after copying to the new file: * 0: must remove old, else remove new and fail, * 1: must remove or truncate old, else remove new and fail, * 2: just leave old if it cannot be removed or truncated. * * Returns positive value for success, negative for failure: * 0: file renamed (link) * 1: file copied, old removed * 2: file copied, but old file is only truncated. * 3: file copied, but old file still exist. * -1: source file not found * -2: cannot create destination * -3: write error * -4: cannot unlink/truncate old * -5: cannot unlink new * -6: cannot link old to new * -9: messy situation: old and new linked on return (cannot happen?) */ move_file(old, new, may_keep_old) char *old, *new; int may_keep_old; { int32 n; if (file_exist(new, (char *)NULL)) { if (file_exist((char *)NULL, "d")) return -5; if (unlink(new) < 0) /* careful - new may be directory ? */ switch (errno) { case ENOENT: break; case EACCES: if (file_exist((char *)NULL, "w")) goto do_copy; default: return -5; } } if (link(old, new) < 0) switch (errno) { case EACCES: /* can just as well try to copy */ case EXDEV: goto do_copy; default: return -6; } if (unlink(old) == 0) return 0; /* we were able to link but not unlink old */ /* remove new, and attempt a copy instead */ if (unlink(new) < 0) return -9; /* cannot happen? */ do_copy: if ((n = copy_file(old, new, 0)) < 0) return n; if (unlink(old) == 0) return 1; if (may_keep_old) if (n == 0 || truncate(old, (off_t)0) == 0) return 2; if (may_keep_old == 2) return 3; unlink(new); return -4; } save_old_file(name, suffix) char *name, *suffix; { char buf[FILENAME]; sprintf(buf, "%s%s", name, suffix); return move_file(name, buf, 0); } #ifdef HAVE_SYSLOG #include <syslog.h> #endif /* HAVE_SYSLOG */ static enter_log(type, va_tail) char type; va_tdcl { FILE *log; char *msg, buf[512]; if (log_entry_filter != NULL) for (msg = log_entry_filter; *msg; msg++) if (*msg == type) return 1; msg = va_arg1(char *); vsprintf(buf, msg, va_args2toN); /* cannot use relative: one of the args may be generated by it */ log = open_file(log_file, OPEN_APPEND); if (log == NULL) return 0; fprintf(log, "%c: %s (%s): %s\n", type, date_time((time_t)0), user_name(), buf); fclose(log); return 1; } static mail_sys_error(err, isfatal) char *err; int isfatal; { FILE *f; char cmd[FILENAME*2]; switch (mail_errors_mode) { case 0: return; case 1: if (!isfatal) return; default: break; } #ifdef FATAL_ERROR_MAIL_CMD strcpy(cmd, FATAL_ERROR_MAIL_CMD); #else #ifdef MAILX sprintf(cmd, "%s -s 'nnmaster %s' %s", MAILX, isfatal ? "fatal error" : "warning", OWNER); #else sprintf(cmd, "mail %s", OWNER); #endif #endif if ((f = popen(cmd, "w")) == NULL) return; fprintf(f, "nnmaster %s\n\n%system error:\n%s\n", isfatal ? "terminated" : "warning", isfatal ? "Fatal s" : "S", err); pclose(f); } /*VARARGS*/ sys_error(va_alist) va_dcl { char buf[512]; char *fmt; FILE *f; use_vararg; start_vararg; enter_log('E', va_args1toN); end_vararg; start_vararg; fmt = va_arg1(char *); vsprintf(buf, fmt, va_args2toN); end_vararg; if (who_am_i == I_AM_MASTER) { mail_sys_error(buf, 1); if (dont_write_console) nn_exit(7); #ifndef HAVE_SYSLOG f = open_file("/dev/console", OPEN_CREATE); if (f == NULL) nn_exit(8); fprintf(f, "\n\rNNMASTER FATAL ERROR\n\r%s\n\n\r", buf); fclose(f); #else /* HAVE_SYSLOG */ openlog("nnmaster", LOG_CONS, LOG_DAEMON); syslog(LOG_ALERT, "%s", buf); closelog(); #endif /* HAVE_SYSLOG */ nn_exit(7); } user_error("%s", buf); } /* * sys_warning: like sys_error but MASTER will return with -1 * instead of exit. Clients still terminate! */ /*VARARGS*/ sys_warning(va_alist) va_dcl { char buf[512]; char *fmt; FILE *f; static char *last_err = NULL; use_vararg; start_vararg; fmt = va_arg1(char *); vsprintf(buf, fmt, va_args2toN); end_vararg; if (last_err != NULL) { if (strcmp(last_err, buf) == 0) return; free(last_err); } last_err = copy_str(buf); start_vararg; enter_log('R', va_args1toN); end_vararg; if (who_am_i != I_AM_MASTER) user_error("%s", buf); mail_sys_error(buf, 0); if (dont_write_console) return -1; #ifndef HAVE_SYSLOG if((f = open_file("/dev/console", OPEN_CREATE)) != NULL) { fprintf(f, "\n\rNNMASTER WARNING\n\r%s\n\n\r", buf); fclose(f); } #else /* HAVE_SYSLOG */ openlog("nnmaster", LOG_CONS, LOG_DAEMON); syslog(LOG_ALERT, "%s", buf); closelog(); #endif /* HAVE_SYSLOG */ return -1; } /*VARARGS*/ log_entry(va_alist) va_dcl { int type, rval; use_vararg; start_vararg; type = va_arg1(int); rval = enter_log(type, va_args2toN); end_vararg; return rval; } char *user_name() { static char *user = NULL; struct passwd *pw, *getpwuid(); extern char *getlogin(), *getenv(); if (who_am_i == I_AM_MASTER) return "M"; if (who_am_i == I_AM_EXPIRE) return "X"; if (user == NULL) { user = getlogin(); if (user != NULL && *user != NUL) goto out; pw = getpwuid((int)user_id); if (pw != NULL && pw->pw_name[0] != NUL) { user = copy_str(pw->pw_name); goto out; } user = getenv("LOGNAME"); if (user != NULL && *user != NUL) goto out; user = getenv("USER"); if (user != NULL && *user != NUL) goto out; user = "?"; } out: return user; } time_t cur_time() { time_t t; time(&t); return t; } char *date_time(t) time_t t; { char *str; if (t == (time_t)0) t = cur_time(); str = ctime(&t); str[16] = 0; return str+4; } char *plural(n) long n; { return n != 1 ? "s" : ""; } /* * memory management */ /* #define MEM_DEBUG /* trace memory usage */ static mem_error(t, bytes) int t; int32 bytes; { char buf[200]; if (t == 1) { sprintf(buf, "Alloc failed: unsigned too short to represent %ld bytes", (long)bytes); } else { sprintf(buf, "Out of memory - cannot allocate %ld bytes", (long)bytes); } sys_error(buf); } char *mem_obj(size, nelt) unsigned size; int32 nelt; { unsigned n; char *obj, *calloc(); n = nelt; if (n != nelt) mem_error(1, nelt); obj = calloc(n, size); #ifdef MEM_DEBUG printf("CALLOC(%u,%u) => %lx\n", n, size, (long)obj); #endif if (obj == NULL) mem_error(2, (int32)(size * nelt)); return obj; } char *mem_str(nelt) int32 nelt; { unsigned n; char *obj, *malloc(); n = nelt; if (n != nelt) mem_error(1, nelt); obj = malloc(n); #ifdef MEM_DEBUG printf("MALLOC(%u) => %lx\n", n, (long)obj); #endif if (obj == NULL) mem_error(2, nelt); return obj; } char *mem_resize(obj, size, nelt) char *obj; unsigned size; int32 nelt; { unsigned n; char *realloc(), *obj1; if (obj == NULL) return mem_obj(size, nelt); nelt *= size; n = nelt; if (n != nelt) mem_error(1, nelt); obj1 = realloc(obj, n); #ifdef MEM_DEBUG printf("REALLOC(%lx, %u) => %lx\n", (long)obj, n, (long)obj1); #endif if (obj1 == NULL) mem_error(2, (int32)size); return obj1; } char *mem_clear(obj, size, nelt) register char *obj; unsigned size; register int32 nelt; { nelt *= size; while (--nelt >= 0) *obj++ = NUL; } mem_free(obj) char *obj; { #ifdef MEM_DEBUG printf("FREE(%lx)\n", (long)obj); #endif if (obj != NULL) free(obj); } #ifndef HAVE_MKDIR mkdir(path, mode) char *path; int mode; { char command[FILENAME*2 + 20]; sprintf(command, "{ mkdir %s && chmod %o %s ; } > /dev/null 2>&1", path, mode, path); return system(command) != 0 ? -1 : 0; } #endif #ifndef HAVE_TRUNCATE truncate(path, len) char *path; off_t len; { int fd; struct stat st; if (len != 0) sys_error("truncate(%s,%ld): non-zero length", path, (long)len); #ifdef O_TRUNC fd = open(path, O_WRONLY | O_TRUNC); #else if (stat(path, &st) < 0) return -1; fd = creat(path, st.st_mode & 07777); #endif if (fd < 0) return -1; close(fd); return 0; } #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.