home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / global.c < prev    next >
C/C++ Source or Header  |  1996-08-14  |  23KB  |  1,193 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Global declarations and auxiliary functions.
  5.  */
  6.  
  7. #include <signal.h>
  8. #include <pwd.h>
  9. #include "config.h"
  10. #include "patchlevel.h"
  11.  
  12. /* global.c */
  13.  
  14. static sig_type catch_keyboard __APROTO((int n));
  15. static sig_type catch_pipe __APROTO((int n));
  16. static sig_type catch_suspend __APROTO((int n));
  17. static enter_log __APROTO((int type, va_list zZap));
  18. static void mail_sys_error __APROTO((char *err, int isfatal));
  19. static void mem_error __APROTO((int t, int32 bytes));
  20.  
  21. export char *home_directory;
  22. export char *nn_directory;        /* ~/.nn */
  23. export char *news_directory;        /* /usr/spool/news */
  24. export char *news_lib_directory;     /* /usr/lib/news */
  25. export char *lib_directory;        /* /usr/local/lib/nn */
  26. export char *master_directory;        /* = lib */
  27. export char *help_directory;        /* = lib/help */
  28. export char *bin_directory = BIN_DIRECTORY;
  29.  
  30. export char *db_directory;    /* /usr/spool/nn or NEWS_DIR/.nn */
  31. export char *db_data_directory;    /* ..../DATA     or undefined    */
  32. export int db_data_subdirs = 0;    /* set if DATA/[0-9]/ exist     */
  33.  
  34. export char *news_active;    /* NLIB/active or DB/ACTIVE */
  35.  
  36. export char *pager;
  37.  
  38. export char *log_file;            /* = lib/Log */
  39. export char *log_entry_filter = NULL;
  40.  
  41. export char *temp_file;
  42.  
  43. #ifndef TMP_DIRECTORY
  44. #define TMP_DIRECTORY "/usr/tmp"
  45. #endif
  46. export char *tmp_directory = TMP_DIRECTORY;
  47.  
  48. export char version_id[100];
  49.  
  50. #ifdef NNTP
  51. export int use_nntp = 0;    /* bool: t iff we use nntp */
  52. #endif
  53.  
  54. export unsigned short user_eid;
  55. export unsigned short user_id, group_id;
  56. export int process_id;
  57. export int who_am_i;
  58. export int dont_write_console = 0;
  59. export int mail_errors_mode = 2;
  60.  
  61. extern int errno;
  62. struct passwd *getpwuid();
  63. extern char *getlogin(), *getenv();
  64.  
  65.  
  66. #ifdef HAVE_MULTIGROUP
  67. #ifndef NGROUPS
  68. #include <sys/param.h>
  69. #endif
  70. #ifndef GIDSET_TYPE
  71. #define GIDSET_TYPE int
  72. #endif
  73. static int ngroups;
  74. static GIDSET_TYPE gidset[NGROUPS];
  75.  
  76. static in_grplist(gid)
  77. GIDSET_TYPE gid;
  78. {
  79.     int n;
  80.  
  81.     if (gid == group_id) return 1;
  82.  
  83.     for (n = 0; n < ngroups; ++n)
  84.     if (gidset[n] == gid) return 1;
  85.  
  86.     return 0;
  87. }
  88.  
  89. #define group_access(gpid)    in_grplist((GIDSET_TYPE)(gpid))
  90. #else
  91. #define group_access(gid)    ((gid) == group_id)
  92. #endif
  93.  
  94. /* signal handler interface */
  95.  
  96. export int s_hangup        = 0;    /* hangup signal */
  97. export int s_keyboard        = 0;    /* keyboard interrupt */
  98. export int s_pipe        = 0;    /* broken pipe */
  99. export int s_redraw        = 0;    /* redraw signal (if job control) */
  100. #ifdef RESIZING
  101. export int s_resized        = 0;    /* screen resized */
  102. #endif
  103.  
  104. #ifdef FAKE_INTERRUPT
  105. #include <setjmp.h>
  106.  
  107. export jmp_buf fake_keyb_sig;
  108. export int arm_fake_keyb_sig = 0;
  109. export char fake_keyb_siglist[NSIG];
  110. #endif
  111.  
  112. sig_type catch_hangup(n)
  113. int n;
  114. {
  115.     s_hangup = 1;
  116.     signal(n, SIG_IGN);
  117.  
  118. #ifdef FAKE_INTERRUPT
  119.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  120.         longjmp(fake_keyb_sig, 1);
  121. #endif
  122. }
  123.  
  124. static sig_type catch_keyboard(n)
  125. int n;
  126. {
  127. #ifdef RESET_SIGNAL_WHEN_CAUGHT
  128.     signal(n, catch_keyboard);
  129. #endif
  130. #ifdef FAKE_INTERRUPT
  131.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  132.     longjmp(fake_keyb_sig, 1);
  133. #endif
  134.     s_keyboard++;
  135. }
  136.  
  137. static sig_type catch_pipe(n)
  138. int n;
  139. {
  140.     s_pipe++;
  141.  
  142. #ifdef RESET_SIGNAL_WHEN_CAUGHT
  143.     signal(n, catch_pipe);
  144. #endif
  145. #ifdef FAKE_INTERRUPT
  146.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  147.         longjmp(fake_keyb_sig, 1);
  148. #endif
  149. }
  150.  
  151. #ifdef HAVE_JOBCONTROL
  152. static sig_type catch_suspend(n)
  153. int n;
  154. {
  155.     s_redraw++;
  156.  
  157. #ifdef RESET_SIGNAL_WHEN_CAUGHT
  158.     signal(n, catch_suspend);
  159. #endif
  160.  
  161.     suspend_nn();
  162.  
  163. #ifdef FAKE_INTERRUPT
  164.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig) {
  165. #ifdef RESIZING
  166.     s_resized++;
  167. #endif
  168.     longjmp(fake_keyb_sig, 1);
  169.     }
  170. #endif
  171. }
  172. #endif
  173.  
  174.  
  175. int
  176. init_global()
  177. {
  178. #ifndef HAVE_UNISTD_H
  179.     unsigned short getuid();
  180.     int getpid();
  181. #endif
  182. #ifdef NOV
  183.     char *nov_id = " (NOV)";
  184. #else
  185.     char *nov_id = "";
  186. #endif
  187.  
  188. #ifdef FAKE_INTERRUPT
  189.     for (i = 0; i < NSIG; i++) fake_keyb_siglist[i] = i == 2 ? 1 : 0;
  190. #endif
  191.  
  192.     if (who_am_i != I_AM_NN) {
  193.     signal(SIGINT,  SIG_IGN);
  194.     signal(SIGQUIT, SIG_IGN);
  195.     }
  196.     signal(SIGTERM, catch_hangup);
  197.     signal(SIGHUP,  catch_hangup);
  198.     signal(SIGPIPE, catch_pipe);
  199.     signal(SIGALRM, SIG_IGN);
  200.  
  201. #ifdef SIGPWR
  202.     signal(SIGPWR, catch_hangup);
  203. #endif
  204.  
  205. #ifdef CONFIG_NUM_IN_VERSION
  206.     sprintf(version_id, "%s.%s #%d%s", RELEASE, PATCHLEVEL,
  207. #include "update.h"
  208.         ,nov_id);
  209. #else
  210.     sprintf(version_id, "%s.%s%s", RELEASE, PATCHLEVEL, nov_id);
  211. #endif
  212.  
  213.     user_id = getuid();
  214.  
  215. #ifdef HAVE_MULTIGROUP
  216.     ngroups = getgroups(NGROUPS, gidset);    /* Get users's group set */
  217. #endif
  218.     group_id = getegid();
  219.     user_eid = geteuid();
  220.  
  221.     process_id = getpid();
  222.  
  223. #ifdef CLIENT_DIRECTORY
  224.     lib_directory = CLIENT_DIRECTORY;
  225. #else
  226.     lib_directory = LIB_DIRECTORY;
  227. #endif
  228.  
  229. #ifdef NEWS_DIRECTORY
  230.     news_directory = NEWS_DIRECTORY;
  231. #else
  232.     news_directory = "/usr/spool/news";
  233. #endif
  234.  
  235. #ifdef DB_DIRECTORY
  236.     db_directory = DB_DIRECTORY;
  237. #else
  238.     db_directory = mk_file_name(lib_directory, ".nn");
  239. #endif
  240.  
  241. #ifdef ACCOUNTING
  242.     if (who_am_i == I_AM_ACCT)
  243.     return 0;
  244. #endif
  245.  
  246. #ifdef DB_DATA_DIRECTORY
  247.     db_data_directory = DB_DATA_DIRECTORY;
  248. #else
  249. #ifdef DB_DIRECTORY
  250.     db_data_directory = mk_file_name(db_directory, "DATA");
  251. #else
  252.     db_data_directory = NULL;
  253. #endif
  254. #endif
  255. #ifndef DB_LONG_NAMES
  256.     if (db_data_directory != NULL)
  257.     db_data_subdirs = file_exist(relative(db_data_directory, "0"), "dx");
  258. #endif
  259.  
  260. #ifdef NEWS_LIB_DIRECTORY
  261.     news_lib_directory = NEWS_LIB_DIRECTORY;
  262. #else
  263.     news_lib_directory = "/usr/lib/news";
  264. #endif
  265.  
  266.     /* this may later be changed by nntp_check */
  267.     news_active = mk_file_name(news_lib_directory, "active");
  268.  
  269. #ifdef MASTER_DIRECTORY
  270.     master_directory = MASTER_DIRECTORY;
  271. #else
  272.     master_directory = LIB_DIRECTORY;
  273. #endif
  274.  
  275. #ifdef HELP_DIRECTORY
  276.     help_directory = HELP_DIRECTORY;
  277. #else
  278.     help_directory = mk_file_name(lib_directory, "help");
  279. #endif
  280.  
  281. #ifdef LOG_FILE
  282.     log_file = LOG_FILE;
  283. #else
  284.     log_file = mk_file_name(LIB_DIRECTORY, "Log");
  285. #endif
  286.  
  287.     if (who_am_i == I_AM_MASTER || who_am_i == I_AM_SPEW)
  288.     return 0;
  289.  
  290.     signal(SIGINT,  catch_keyboard);
  291.     signal(SIGQUIT, catch_keyboard);
  292. #ifdef HAVE_JOBCONTROL
  293.     signal(SIGTSTP, catch_suspend);
  294. #endif
  295.  
  296.     if ((home_directory = getenv("HOME")) == NULL)
  297.     nn_exitmsg(1, "No HOME environment variable");
  298.  
  299.     if ((pager = getenv("PAGER")) == NULL)
  300.     pager = DEFAULT_PAGER;
  301.  
  302.     nn_directory = mk_file_name(home_directory, ".nn");
  303.  
  304.     if (who_am_i != I_AM_ADMIN && !file_exist(nn_directory, "drwx")) {
  305.     if (who_am_i != I_AM_NN) return -1;
  306.     if (mkdir(nn_directory, 0755) < 0)
  307.         nn_exitmsg(1, "Cannot create %s directory", nn_directory);
  308.     return 1;
  309.     }
  310.  
  311.     return 0;
  312. }
  313.  
  314. void
  315. new_temp_file()
  316. {
  317.     static char buf[FILENAME];
  318.     static char *temp_dir = NULL;
  319.  
  320.     if (temp_dir == NULL)
  321.     if ((temp_dir = getenv("TMPDIR")) == NULL)
  322.         temp_dir = tmp_directory; /* just to make test above false */
  323.     else
  324.         tmp_directory = temp_dir;
  325.  
  326.     sprintf(buf, "%s/nn.XXXXXX", tmp_directory);
  327.     mktemp(buf);
  328.     temp_file = buf;
  329. }
  330.  
  331.  
  332. FILE *open_file(name, mode)
  333. char *name;
  334. int mode;
  335. {
  336.     FILE *f = NULL;
  337.     int fd;
  338.  
  339.     if ((mode & DONT_CREATE) && !file_exist(name, (char *)NULL))
  340.     return NULL;
  341.  
  342.     switch (mode & 0x0f) {
  343.  
  344.      case OPEN_READ:
  345.  
  346.     f = fopen(name, "r");
  347.     break;
  348.  
  349.      case OPEN_UPDATE:
  350.  
  351. /*    f = fopen(name, "r+");     -- not reliable on many systems (sigh) */
  352.  
  353.     if ((fd = open(name, O_WRONLY)) >= 0) {
  354.         if ((f = fdopen(fd, "w")) != NULL) return f;
  355.         close(fd);
  356.     }
  357.  
  358.     /* FALL THRU */
  359.      case OPEN_CREATE:
  360.  
  361.     f = fopen(name, "w");
  362.     break;
  363.  
  364.      case OPEN_APPEND:
  365.  
  366.     f = fopen(name, "a");
  367.     break;
  368.  
  369.      case OPEN_CREATE_RW:
  370.  
  371.     f = fopen(name, "w+");    /* not safe on all systems -- beware */
  372.     break;
  373.  
  374.      default:
  375.  
  376.     sys_error("Illegal mode: open_file(%s, 0x%x)", name, mode);
  377.     }
  378.  
  379.     if (f) {
  380.     if (mode & OPEN_UNLINK) unlink(name);
  381.     return f;
  382.     }
  383.  
  384.     if ((mode & MUST_EXIST) == 0) return NULL;
  385.  
  386.     sys_error("Cannot open file %s, mode=0x%x, errno=%d", name, mode, errno);
  387.  
  388.     return NULL;
  389. }
  390.  
  391. FILE *open_file_search_path(name, mode)
  392. char *name;
  393. int mode;
  394. {
  395.     FILE *f;
  396.  
  397.     if (name == NULL) return NULL;
  398.  
  399.     if (*name == '/') return open_file(name, mode);
  400.     
  401. #ifdef NNTP
  402.     f = NULL;
  403. #endif    /* NNTP */
  404.     if (!use_nntp)
  405.     f = open_file(relative(news_lib_directory, name), OPEN_READ);
  406.     if (f == NULL)
  407.     f= open_file(relative(lib_directory, name), OPEN_READ);
  408.     if (f == NULL)
  409.     f = open_file(relative(db_directory, name), OPEN_READ);
  410.  
  411.     return f;
  412. }
  413.  
  414. int    
  415. fgets_multi(buf, size, f)
  416. char *buf;
  417. int size;
  418. register FILE *f;
  419. {
  420.     register char *s = buf;
  421.     register int c, n = size;
  422.     
  423.     while (--n > 0) {
  424.     c = getc(f);
  425.     if (c == '\\') {
  426.         if ((c = getc(f)) == NL) continue;
  427.         *s++ = '\\';
  428.         if (--n < 0) break;
  429.     }
  430.     if (c == EOF) {
  431.         *s = NUL;
  432.         return s != buf;
  433.     }
  434.     if (c == NL) {
  435.         *s = NUL;
  436.         return 1;
  437.     }
  438.     *s++ = c;
  439.     }
  440.     buf[30] = NUL;
  441.     sys_error("Line too long \"%s...\" (max %d)", buf, size);
  442.     return 0;
  443. }
  444.     
  445. /*
  446.  *     relative -- concat directory name and file name
  447.  */
  448.  
  449. char *relative(dir, name)
  450. char *dir, *name;
  451. {
  452.     static char concat_path[FILENAME];
  453.  
  454.     sprintf(concat_path, "%s/%s", dir, name);
  455.     return concat_path;
  456. }
  457.  
  458.  
  459. char *mk_file_name(dir, name)
  460. char *dir, *name;
  461. {
  462.     char *buf;
  463.  
  464.     buf = newstr(strlen(dir) + strlen(name) + 2);
  465.     sprintf(buf, "%s/%s", dir, name);
  466.  
  467.     return buf;
  468. }
  469.  
  470.  
  471. char *home_relative(dir)
  472. char *dir;
  473. {
  474.     if (dir) {
  475.     if (*dir == '/')
  476.         return copy_str(dir);
  477.     else {
  478.         if (*dir == '~' && *++dir == '/') dir++;
  479.         return mk_file_name(home_directory, dir);
  480.     }
  481.     }
  482.     return NULL;
  483. }
  484.  
  485.  
  486. char *substchr(str, c1, c2)
  487. char *str, c1, c2;
  488. {
  489.     char *p;
  490.  
  491.     if ((p = strchr(str, c1)) != NULL) *p = c2;
  492.     return p;
  493. }
  494.  
  495. char *copy_str(str)
  496. char *str;
  497. {
  498.     char *new;
  499.  
  500.     new = newstr(strlen(str) + 1);
  501.     if (new) strcpy(new, str);
  502.  
  503.     return new;
  504. }
  505.  
  506. time_t m_time(f)
  507. FILE *f;
  508. {
  509.     struct stat st;
  510.  
  511.     if (fstat(fileno(f), &st) < 0) return 0;
  512.     return st.st_mtime;
  513. }
  514.  
  515.  
  516. time_t file_exist(name, mode)
  517. char *name;
  518. char *mode;
  519. {
  520.     static struct stat statb;
  521.     int mask;
  522.  
  523.     if (name != NULL && stat(name, &statb)) return 0;
  524.  
  525.     if (mode == NULL) return statb.st_mtime;
  526.  
  527.     if (statb.st_uid == user_eid)
  528.     mask = 0700;
  529.     else if (group_access(statb.st_gid))
  530.     mask = 0070;
  531.     else
  532.     mask = 0007;
  533.  
  534.     while (*mode) {
  535.     switch (*mode++) {
  536.     case 'd':
  537.         if ((statb.st_mode & S_IFMT) == S_IFDIR) continue;
  538.         errno = ENOTDIR;
  539.         return 0;
  540.     case 'f':
  541.         if ((statb.st_mode & S_IFMT) == S_IFREG) continue;
  542.         if ((statb.st_mode & S_IFMT) == 0000000) continue;
  543.         if ((statb.st_mode & S_IFMT) == S_IFDIR) {
  544.         errno = EISDIR;
  545.         return 0;
  546.         }
  547.         break;
  548.     case 'r':
  549.         if (statb.st_mode & mask & 0444) continue;
  550.         break;
  551.     case 'w':
  552.         if (statb.st_mode & mask & 0222) continue;
  553.         break;
  554.     case 'x':
  555.         if (statb.st_mode & mask & 0111) continue;
  556.         break;
  557.     }
  558.     errno = EACCES;
  559.     return 0;
  560.     }
  561.  
  562.     /* all modes are ok */
  563.     return statb.st_mtime;
  564. }
  565.  
  566. /*
  567.  * copy_file: copy (or append) src file to dest file.
  568.  *
  569.  * Returns number of characters copied or an error code:
  570.  *  -1: source file not found
  571.  *  -2: cannot create destination
  572.  *  -3: write error
  573.  */
  574.  
  575. int32 copy_file(src, dest, append)
  576. char *src, *dest;
  577. int append;
  578. {
  579.     register FILE *s, *d;
  580.     register int32 n;
  581.     register int c;
  582.  
  583.     s = open_file(src, OPEN_READ);
  584.     if (s == NULL) return -1;
  585.  
  586.     d = open_file(dest, append ? OPEN_APPEND : OPEN_CREATE);
  587.     if (d == NULL) {
  588.     fclose(s);
  589.     return -2;
  590.     }
  591.  
  592.     n = 0;
  593.     while ((c = getc(s)) != EOF) {
  594.     putc(c, d);
  595.     n++;
  596.     }
  597.  
  598.     fclose(s);
  599.     if (fclose(d) == EOF) {
  600.     if (!append) unlink(dest);
  601.     return -3;
  602.     }
  603.     return n;
  604. }
  605.  
  606. /*
  607.  * move_file: move old file to new file, linking if possible.
  608.  *
  609.  * The third arg determines what is acceptable if the old file cannot be
  610.  * removed after copying to the new file:
  611.  *   0: must remove old, else remove new and fail,
  612.  *   1: must remove or truncate old, else remove new and fail,
  613.  *   2: just leave old if it cannot be removed or truncated.
  614.  *    
  615.  * Returns positive value for success, negative for failure:
  616.  *   0: file renamed (link)
  617.  *   1: file copied, old removed
  618.  *   2: file copied, but old file is only truncated.
  619.  *   3: file copied, but old file still exist.
  620.  *  -1: source file not found
  621.  *  -2: cannot create destination
  622.  *  -3: write error
  623.  *  -4: cannot unlink/truncate old
  624.  *  -5: cannot unlink new
  625.  *  -6: cannot link old to new
  626.  *  -9: messy situation: old and new linked on return (cannot happen?)
  627.  */
  628.  
  629. int
  630. move_file(old, new, may_keep_old)
  631. char *old, *new;
  632. int may_keep_old;
  633. {
  634.     int32 n;
  635.  
  636.     if (file_exist(new, (char *)NULL)) {
  637.     if (file_exist((char *)NULL, "d"))
  638.         return -5;
  639.     if (unlink(new) < 0)    /* careful - new may be directory ? */
  640.         switch (errno) {
  641.          case ENOENT:
  642.         break;
  643.          case EACCES:
  644.         if (file_exist((char *)NULL, "w")) goto do_copy;
  645.          default:
  646.         return -5;
  647.         }
  648.     }
  649.     
  650.     if (link(old, new) < 0)
  651.     switch (errno) {
  652.      case EACCES:    /* can just as well try to copy */
  653.      case EXDEV:
  654.         goto do_copy;
  655.      default:
  656.         return -6;
  657.     }
  658.     
  659.     if (unlink(old) == 0)
  660.     return 0;
  661.  
  662.     /* we were able to link but not unlink old    */
  663.     /* remove new, and attempt a copy instead    */
  664.     if (unlink(new) < 0) return -9; /* cannot happen? */
  665.  
  666.  do_copy:
  667.     if ((n = copy_file(old, new, 0)) < 0) return n;
  668.     if (unlink(old) == 0) return 1;
  669.     if (may_keep_old)
  670.     if (n == 0 || nn_truncate(old, (off_t)0) == 0) return 2;
  671.     if (may_keep_old == 2) return 3;
  672.     unlink(new);
  673.     return -4;
  674. }
  675. int
  676. save_old_file(name, suffix)
  677. char *name, *suffix;
  678. {
  679.     char buf[FILENAME];
  680.     sprintf(buf, "%s%s", name, suffix);
  681.     return move_file(name, buf, 0);
  682. }
  683.  
  684. #ifdef HAVE_SYSLOG
  685. #include <syslog.h>
  686. #endif /* HAVE_SYSLOG */
  687.  
  688.  
  689. static int
  690. enter_log(type, va_tail)
  691. char type;
  692. va_tdcl
  693. {
  694.     FILE *log;
  695.     char *msg1, buf[512];
  696.  
  697.     if (log_entry_filter != NULL)
  698.     for (msg1 = log_entry_filter; *msg1; msg1++)
  699.         if (*msg1 == type) return 1;
  700.  
  701.     msg1  = va_arg1(char *);
  702.     vsprintf(buf, msg1, va_args2toN);
  703.  
  704.     /* cannot use relative: one of the args may be generated by it */
  705.  
  706.     log = open_file(log_file, OPEN_APPEND);
  707.     if (log == NULL) return 0;
  708.  
  709.     fprintf(log, "%c: %s (%s): %s\n", type,
  710.         date_time((time_t)0), user_name(), buf);
  711.  
  712.     fclose(log);
  713.     return 1;
  714. }
  715.  
  716. static void
  717. mail_sys_error(err, isfatal)
  718. char *err;
  719. int isfatal;
  720. {
  721.     FILE *f;
  722.     char cmd[FILENAME*2];
  723.  
  724.     switch (mail_errors_mode) {
  725.      case 0:
  726.     return;
  727.      case 1:
  728.     if (!isfatal) return;
  729.      default:
  730.     break;
  731.     }
  732.     
  733. #ifdef FATAL_ERROR_MAIL_CMD
  734.     strcpy(cmd, FATAL_ERROR_MAIL_CMD);
  735. #else
  736. #ifdef MAILX
  737.     sprintf(cmd, "%s -s 'nnmaster %s' %s", MAILX,
  738.         isfatal ? "fatal error" : "warning", OWNER);
  739. #else
  740.     sprintf(cmd, "mail %s", OWNER);
  741. #endif
  742. #endif
  743.     if ((f = popen(cmd, "w")) == NULL) return;
  744.     fprintf(f, "nnmaster %s\n\n%system error:\n%s\n",
  745.         isfatal ? "terminated" : "warning",
  746.         isfatal ? "Fatal s" : "S", err);
  747.     pclose(f);
  748. }
  749.  
  750. /*VARARGS*/
  751. void sys_error(va_alist)
  752. va_dcl
  753. {
  754.     char buf[512];
  755.     char *fmt;
  756. #ifndef HAVE_SYSLOG
  757.     FILE *f;
  758. #endif
  759.     use_vararg;
  760.  
  761.     start_vararg;
  762.     enter_log('E', va_args1toN);
  763.     end_vararg;
  764.  
  765.     start_vararg;
  766.     fmt = va_arg1(char *);
  767.     vsprintf(buf, fmt, va_args2toN);
  768.     end_vararg;
  769.  
  770.     if (who_am_i == I_AM_MASTER) {
  771.     mail_sys_error(buf, 1);
  772.     if (dont_write_console) nn_exit(7);
  773. #ifndef HAVE_SYSLOG
  774.     f = open_file("/dev/console", OPEN_CREATE);
  775.     if (f == NULL) nn_exit(8);
  776.     fprintf(f, "\n\rNNMASTER FATAL ERROR\n\r%s\n\n\r", buf);
  777.     fclose(f);
  778. #else /* HAVE_SYSLOG */
  779.     openlog("nnmaster", LOG_CONS, LOG_DAEMON);
  780.     syslog(LOG_ALERT, "%s", buf);
  781.     closelog();
  782. #endif /* HAVE_SYSLOG */
  783.     nn_exit(7);
  784.     }
  785.     nn_exitmsg(1, "%s", buf);
  786. }
  787.  
  788. /*
  789.  *    sys_warning: like sys_error but MASTER will return with -1
  790.  *    instead of exit.  Clients still terminate!
  791.  */
  792.  
  793. /*VARARGS*/
  794. int
  795. sys_warning(va_alist)
  796. va_dcl
  797. {
  798.     char buf[512];
  799.     char *fmt;
  800. #ifndef HAVE_SYSLOG
  801.     FILE *f;
  802. #endif
  803.     static char *last_err = NULL;
  804.     use_vararg;
  805.  
  806.     start_vararg;
  807.     fmt = va_arg1(char *);
  808.     vsprintf(buf, fmt, va_args2toN);
  809.     end_vararg;
  810.  
  811.     if (last_err != NULL) {
  812.     if (strcmp(last_err, buf) == 0) return -1;
  813.     free(last_err);
  814.     }
  815.     last_err = copy_str(buf);
  816.  
  817.     start_vararg;
  818.     enter_log('R', va_args1toN);
  819.     end_vararg;
  820.  
  821.     if (who_am_i != I_AM_MASTER)
  822.     nn_exitmsg(1, "%s", buf);
  823.  
  824.     mail_sys_error(buf, 0);
  825.     if (dont_write_console) return -1;
  826. #ifndef HAVE_SYSLOG
  827.     if((f = open_file("/dev/console", OPEN_CREATE)) != NULL) {
  828.     fprintf(f, "\n\rNNMASTER WARNING\n\r%s\n\n\r", buf);
  829.     fclose(f);
  830.     }
  831. #else /* HAVE_SYSLOG */
  832.     openlog("nnmaster", LOG_CONS, LOG_DAEMON);
  833.     syslog(LOG_ALERT, "%s", buf);
  834.     closelog();
  835. #endif /* HAVE_SYSLOG */
  836.     return -1;
  837. }
  838.  
  839. /*VARARGS*/
  840. int
  841. log_entry(va_alist)
  842. va_dcl
  843. {
  844.     int type, rval;
  845.     use_vararg;
  846.  
  847.     start_vararg;
  848.     type = va_arg1(int);
  849.     rval = enter_log(type, va_args2toN);
  850.     end_vararg;
  851.  
  852.     return rval;
  853. }
  854.  
  855. char *user_name()
  856. {
  857.     static char *user = NULL;
  858.     struct passwd *pw;
  859.  
  860.     if (who_am_i == I_AM_MASTER) return "M";
  861.     if (who_am_i == I_AM_EXPIRE) return "X";
  862.  
  863.     if (user == NULL) {
  864.     user = getlogin();
  865.     if (user != NULL && *user != NUL) goto out;
  866.  
  867.     pw = getpwuid((int)user_id);
  868.     if (pw != NULL && pw->pw_name[0] != NUL) {
  869.         user = copy_str(pw->pw_name);
  870.         goto out;
  871.     }
  872.  
  873.     user = getenv("LOGNAME");
  874.     if (user != NULL && *user != NUL) goto out;
  875.     user = getenv("USER");
  876.     if (user != NULL && *user != NUL) goto out;
  877.     user = "?";
  878.     }
  879.  
  880.  out:
  881.     return user;
  882. }
  883.  
  884. time_t cur_time()
  885. {
  886.     time_t t;
  887.  
  888.     time(&t);
  889.     return t;
  890. }
  891.  
  892. char *date_time(t)
  893. time_t t;
  894. {
  895.     char *str;
  896.  
  897.     if (t == (time_t)0) t = cur_time();
  898.     str = ctime(&t);
  899.  
  900.     str[16] = 0;
  901.     return str+4;
  902. }
  903.  
  904. char *plural(n)
  905. long n;
  906. {
  907.     return n != 1 ? "s" : "";
  908. }
  909.  
  910. /*
  911.  *    memory management
  912.  */
  913.  
  914. /* #define MEM_DEBUG */        /* trace memory usage */
  915.  
  916. static void
  917. mem_error(t, bytes)
  918. int t;
  919. int32 bytes;
  920. {
  921.     char buf[200];
  922.  
  923.     if (t == 1) {
  924.     sprintf(buf, "Alloc failed: unsigned too short to represent %ld bytes",
  925.         (long)bytes);
  926.     } else {
  927.     sprintf(buf, "Out of memory - cannot allocate %ld bytes",
  928.         (long)bytes);
  929.     }
  930.  
  931.     sys_error(buf);
  932. }
  933.  
  934. char *mem_obj(size, nelt)
  935. unsigned size;
  936. int32 nelt;
  937. {
  938.     unsigned n;
  939.     char *obj;
  940. #ifndef HAVE_STDLIB_H
  941.     char *calloc();
  942. #endif
  943.  
  944.     n = nelt;
  945.     if (n != nelt) mem_error(1, nelt);
  946.  
  947.     obj = calloc(n, size);
  948. #ifdef MEM_DEBUG
  949.     printf("CALLOC(%u,%u) => %lx\n", n, size, (long)obj);
  950. #endif
  951.     if (obj == NULL) mem_error(2, (int32)(size * nelt));
  952.     return obj;
  953. }
  954.  
  955. char *mem_str(nelt)
  956. int32 nelt;
  957. {
  958.     unsigned n;
  959.     char *obj;
  960. #ifndef HAVE_STDLIB_H
  961.     char *malloc();
  962. #endif
  963.  
  964.     n = nelt;
  965.     if (n != nelt) mem_error(1, nelt);
  966.  
  967.     obj = malloc(n);
  968. #ifdef MEM_DEBUG
  969.     printf("MALLOC(%u) => %lx\n", n, (long)obj);
  970. #endif
  971.     if (obj == NULL) mem_error(2, nelt);
  972.     return obj;
  973. }
  974.  
  975. char *mem_resize(obj, size, nelt)
  976. char *obj;
  977. unsigned size;
  978. int32 nelt;
  979. {
  980.     unsigned n;
  981.     char *obj1;
  982. #ifndef HAVE_STDLIB_H
  983.     char *realloc();
  984. #endif
  985.  
  986.     if (obj == NULL)
  987.     return mem_obj(size, nelt);
  988.  
  989.     nelt *= size;
  990.  
  991.     n = nelt;
  992.     if (n != nelt) mem_error(1, nelt);
  993.  
  994.     obj1 = realloc(obj, n);
  995. #ifdef MEM_DEBUG
  996.     printf("REALLOC(%lx, %u) => %lx\n", (long)obj, n, (long)obj1);
  997. #endif
  998.     if (obj1 == NULL) mem_error(2, (int32)size);
  999.     return obj1;
  1000. }
  1001.  
  1002.  
  1003. void
  1004. mem_clear(obj, size, nelt)
  1005. register char *obj;
  1006. unsigned size;
  1007. register int32 nelt;
  1008. {
  1009.     nelt *= size;
  1010.     while (--nelt >= 0) *obj++ = NUL;
  1011. }
  1012.  
  1013. void
  1014. mem_free(obj)
  1015. char *obj;
  1016. {
  1017. #ifdef MEM_DEBUG
  1018.     printf("FREE(%lx)\n", (long)obj);
  1019. #endif
  1020.     if (obj != NULL) free(obj);
  1021. }
  1022.  
  1023. #ifndef HAVE_MKDIR
  1024.  
  1025. mkdir(path, mode)
  1026. char *path;
  1027. int mode;
  1028. {
  1029.     char command[FILENAME*2 + 20];
  1030.  
  1031.     sprintf(command, "{ mkdir %s && chmod %o %s ; } > /dev/null 2>&1",
  1032.         path, mode, path);
  1033.     return system(command) != 0 ? -1 : 0;
  1034. }
  1035. #endif
  1036.  
  1037.  
  1038. int
  1039. nn_truncate(path, len)
  1040. char *path;
  1041. off_t len;
  1042. {
  1043. #ifdef HAVE_TRUNCATE
  1044.  
  1045.     /* Easy... we already have it... */
  1046.     return truncate(path, len);
  1047.  
  1048. #else /* HAVE_TRUNCATE */
  1049.  
  1050.     int fd;
  1051. #ifndef O_TRUNC
  1052.     struct stat st;
  1053. #endif
  1054.  
  1055.     if (len != 0)
  1056.     sys_error("nn_truncate(%s,%ld): non-zero length", path, (long)len);
  1057.  
  1058. #ifdef O_TRUNC
  1059.     fd = open(path, O_WRONLY | O_TRUNC);
  1060. #else
  1061.     if (stat(path, &st) < 0) return -1;
  1062.     fd = creat(path, st.st_mode & 07777);
  1063. #endif    
  1064.     if (fd < 0) return -1;
  1065.     close(fd);
  1066.     return 0;
  1067. #endif
  1068. }
  1069.  
  1070. /* Should really have some sort of configure file and use strdup().. */
  1071. char *
  1072. strsave(s)
  1073. char *s;
  1074. {
  1075.     int l;
  1076.     char *buf = NULL;
  1077.     
  1078.     if (s) {
  1079.     l = strlen(s);
  1080.     buf = malloc(l + 1);
  1081.     if (buf) {
  1082.         if (l)
  1083.         strcpy(buf, s);
  1084.         else
  1085.         buf[0] = '\0';
  1086.     }
  1087.     }
  1088.     return buf;
  1089. }
  1090.  
  1091. char *
  1092. str3save(s1, s2, s3)
  1093. char *s1, *s2, *s3;
  1094. {
  1095.     int l;
  1096.     char *buf;
  1097.  
  1098.     if (!s1) s1 = "";
  1099.     if (!s2) s2 = "";
  1100.     if (!s3) s3 = "";
  1101.  
  1102.     l = strlen(s1) + strlen(s2) + strlen(s3);
  1103.  
  1104.     buf = malloc(l + 1);
  1105.     if (buf) {
  1106.     strcpy(buf, s1);
  1107.     strcat(buf, s2);
  1108.     strcat(buf, s3);
  1109.     }
  1110.     return buf;
  1111. }
  1112.  
  1113. /*
  1114.  * This quick function is a hack of a replacement for fgets, but is
  1115.  * unlimited in line length.  It returns a pointer to a buffer which
  1116.  * just happens to be malloc()ed and is reused next time.  IE: you had
  1117.  * best strdup() it. 
  1118.  *
  1119.  * This was inspired by an article I saw in alt.sources but forgot to
  1120.  * save.
  1121.  *
  1122.  * -Peter Wemm <peter@DIALix.oz.au>
  1123.  */
  1124.  
  1125. char    *buf = NULL;    /* buffer for line */
  1126. int    size = 0;    /* size of buffer */
  1127.  
  1128. #define INITIAL_CHUNK    256    /* Initial malloc size */
  1129. #define GROW_CHUNK    256    /* extend with realloc in units if this */
  1130.  
  1131. /* 
  1132.  * Get a LONG line.
  1133.  * Returns a pointer to a '\0' terminated string which will be
  1134.  * overwritten on the next call.
  1135.  * Returns NULL if error or eof, or if nothing could be read at all.
  1136.  *
  1137.  * Note: It does not react very well to '\0' characters in the byte stream
  1138.  */
  1139.  
  1140. char *
  1141. fgetstr(f)
  1142. FILE    *f;
  1143. {    
  1144.   int    len;            /* # of chars stored into buf before '\0' */
  1145.   char    *s;
  1146.  
  1147.   if (size == 0 && buf == NULL) {
  1148.     size = INITIAL_CHUNK;
  1149.     buf = malloc(size);
  1150.     if (buf == NULL) {
  1151.       size = 0;
  1152.       nn_exitmsg(50, "cant allocate initial chunk\n");
  1153.       return NULL;
  1154.     }
  1155.     clearobj(buf, size, 1);
  1156.   }
  1157.     
  1158.   len = 0;
  1159.  
  1160.   while (fgets(buf + len, size - len, f) != NULL)
  1161.   {
  1162.     len += strlen(len + buf);
  1163.     if (len > 0 && buf[len - 1] == '\n')
  1164.       break;            /* the whole line has been read */
  1165.  
  1166.     size += GROW_CHUNK;
  1167.     s = realloc(buf, size);
  1168.     
  1169.     if (!s) {            /* Malloc failure */
  1170.       free(buf);        /* panic... */
  1171.       size = 0;
  1172.       buf = NULL;
  1173.       nn_exitmsg(51, "cant realloc chunk\n");
  1174.       return NULL;
  1175.     }
  1176.     buf = s;
  1177.     clearobj(buf + len, size - len, 1);
  1178.   }
  1179.  
  1180.   if (len == 0) {
  1181.     return NULL;        /* nothing read (eof or error) */
  1182.   }
  1183.  
  1184.   /* For when we are reading from a NNTP stream. */
  1185.   if (len > 1 && buf[len - 1] == '\n')
  1186.     --len;
  1187.   if (len > 1 && buf[len - 1] == '\r')
  1188.     --len;
  1189.   buf[len] = '\0';        /* unconditionally terminate string, */
  1190.                 /* possibly overwriting CR and/or NL */
  1191.   return buf;
  1192. }
  1193.