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