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