1 /* This file is part of GNU Rush.
2    Copyright (C) 2008-2019 Sergey Poznyakoff
3 
4    GNU Rush is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Rush is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Rush.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #include <sys/time.h>
22 #include <sys/stat.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <strftime.h>
29 #include <fprintftime.h>
30 #include <inttostr.h>
31 #include <xalloc.h>
32 #include <c-ctype.h>
33 
34 #include "librush.h"
35 
36 mode_t rushdb_umask = 022;
37 mode_t rushdb_dir_mode = 0777;
38 mode_t rushdb_file_mode = 0666;
39 
40 #define ERROR_BUFFER_SIZE 1024
41 static char rushdb_error_buffer[ERROR_BUFFER_SIZE];
42 char *rushdb_error_string = rushdb_error_buffer;
43 
44 static void format_error(const char *fmt, ...) RUSH_PRINTFLIKE(1,2);
45 
46 static void
format_error(const char * fmt,...)47 format_error(const char *fmt, ...)
48 {
49 	va_list ap;
50 	va_start(ap, fmt);
51 	vsnprintf(rushdb_error_buffer, sizeof(rushdb_error_buffer), fmt, ap);
52 	va_end(ap);
53 }
54 
55 static char *
mkname(const char * dir,const char * file)56 mkname(const char *dir, const char *file)
57 {
58 	char *s = malloc(strlen(dir) + 1 + strlen(file) + 1);
59 	if (s) {
60 		strcpy(s, dir);
61 		strcat(s, "/");
62 		strcat(s, file);
63 	}
64 	return s;
65 }
66 
67 static enum rushdb_result
rushdb_open_internal(const char * dbdir,int rw)68 rushdb_open_internal(const char *dbdir, int rw)
69 {
70 	char *fname;
71 	int rc;
72 	struct stat st;
73 
74 	if (stat(dbdir, &st)) {
75 		if (errno == ENOENT) {
76 			if (!rw)
77 				return rushdb_result_eof;
78 			if (mkdir(dbdir, rushdb_dir_mode)) {
79 				format_error(_("cannot create directory %s: %s"),
80 					     dbdir, strerror(errno));
81 				return rushdb_result_fail;
82 			}
83 		} else {
84 			format_error(_("cannot stat directory %s: %s"),
85 				     dbdir, strerror(errno));
86 			return rushdb_result_fail;
87 		}
88 	} else if (!S_ISDIR(st.st_mode)) {
89 		format_error(_("%s is not a directory"), dbdir);
90 		return rushdb_result_fail;
91 	}
92 
93 	fname = mkname(dbdir, RUSH_UTMP_NAME);
94 	if (!fname) {
95 		format_error("%s", gettext(strerror(ENOMEM)));
96 		return rushdb_result_fail;
97 	}
98 	rc = rush_utmp_open(fname, rw);
99 	if (rc) {
100 		format_error(_("cannot open file %s: %s"),
101 			     fname, strerror(errno));
102 		free(fname);
103 		return rushdb_result_fail;
104 	}
105 	free(fname);
106 
107 	fname = mkname(dbdir, RUSH_WTMP_NAME);
108 	if (!fname) {
109 		format_error("%s", gettext(strerror(ENOMEM)));
110 		return rushdb_result_fail;
111 	}
112 	rc = rush_wtmp_open(fname, rw);
113 	if (rc) {
114 		format_error(_("cannot open file %s: %s"),
115 			     fname, strerror(errno));
116 		free(fname);
117 		return rushdb_result_fail;
118 	}
119 	free(fname);
120 
121 	return rushdb_result_ok;
122 }
123 
124 enum rushdb_result
rushdb_open(const char * dbdir,int rw)125 rushdb_open(const char *dbdir, int rw)
126 {
127 	mode_t um = umask(rushdb_umask);
128 	enum rushdb_result res = rushdb_open_internal(dbdir, rw);
129 	umask(um);
130 	return res;
131 }
132 
133 int
rushdb_close()134 rushdb_close()
135 {
136 	return rush_wtmp_close() || rush_utmp_close();
137 }
138 
139 void
rushdb_backward_direction()140 rushdb_backward_direction()
141 {
142 	rush_wtmp_set_dir(rush_wtmp_backward);
143 }
144 
145 
146 /* Locking */
147 
148 static int lock_typetab[] = {
149 	F_RDLCK,              /* RUSH_LOCK_READ */
150 	F_WRLCK               /* RUSH_LOCK_WRITE */
151 };
152 
153 int
rushdb_lock(int fd,size_t size,off_t offset,int whence,int type)154 rushdb_lock(int fd, size_t size, off_t offset, int whence, int type)
155 {
156 	struct flock fl;
157 
158 	if (type < 0 || type > 1) {
159 		errno = EINVAL;
160 		return -1;
161 	}
162 
163 	fl.l_type = lock_typetab[type];
164 	fl.l_whence = whence;
165 	fl.l_start = offset;
166 	fl.l_len = size;
167 	return fcntl(fd, F_SETLKW, &fl); /* FIXME: Handle EINTR */
168 }
169 
170 int
rushdb_unlock(int fd,size_t size,off_t offset,int whence)171 rushdb_unlock(int fd, size_t size, off_t offset, int whence)
172 {
173 	struct flock fl;
174 
175 	fl.l_type = F_UNLCK;
176 	fl.l_whence = whence;
177 	fl.l_start = offset;
178 	fl.l_len = size;
179 	return fcntl(fd, F_SETLKW, &fl);
180 }
181 
182 
183 #define FDATA_FH      0
184 #define FDATA_STRING  1
185 #define FDATA_TAB     2
186 #define FDATA_NEWLINE 3
187 
188 struct format_key {
189 	struct format_key *next;
190 	char *name;
191 	char *value;
192 };
193 
194 typedef int (*rushdb_format_fp) (int outbytes,
195 				 int width,
196 				 struct format_key *key,
197 				 struct rush_wtmp *);
198 
199 struct rushdb_format {
200 	rushdb_format_t next;
201 	int type;
202 	struct format_key *key;
203 	union {
204 		struct {
205 			rushdb_format_fp fun;
206 			int width;
207 			char *header;
208 		} fh;              /* FDATA_FH */
209 		char *string;      /* FDATA_STRING */
210 		int tabstop;       /* FDATA_TAB */
211 		int nl;            /* FDATA_NEWLINE */
212 	} v;
213 };
214 
215 char *rushdb_date_format = "%a %H:%M";
216 
217 #define ALIGN_LEFT  0
218 #define ALIGN_RIGHT 1
219 #define TAB_SIZE    8
220 
221 
222 /* Key auxiliary */
223 static void
format_key_free(struct format_key * key)224 format_key_free(struct format_key *key)
225 {
226 	struct format_key *next;
227 	while (key) {
228 		next = key->next;
229 		free(key->name);
230 		free(key->value);
231 		free(key);
232 		key = next;
233 	}
234 }
235 
236 static char *
format_key_lookup(struct format_key * key,char * name)237 format_key_lookup(struct format_key *key, char *name)
238 {
239 	for (; key; key = key->next) {
240 		if (strcmp(key->name, name) == 0)
241 			return key->value;
242 	}
243 	return NULL;
244 }
245 
246 static void
form_free(struct rushdb_format * form)247 form_free(struct rushdb_format *form)
248 {
249 	struct rushdb_format *next;
250 
251 	while (form) {
252 		next = form->next;
253 
254 		format_key_free(form->key);
255 		switch (form->type) {
256 		case FDATA_STRING:
257 			free(form->v.string);
258 			break;
259 		case FDATA_FH:
260 			free(form->v.fh.header);
261 			break;
262 		default:
263 			break;
264 		}
265 		free(form);
266 
267 		form = next;
268 	}
269 }
270 
271 static int
key_align(struct format_key * key)272 key_align(struct format_key *key)
273 {
274 	char *p = format_key_lookup(key, "right");
275 	return p ? ALIGN_RIGHT : ALIGN_LEFT;
276 }
277 
278 
279 static int
output_string(char * string,int width,int align)280 output_string(char *string, int width, int align)
281 {
282 	if (width == 0)
283 		width = printf("%s", string);
284 	else if (align == ALIGN_LEFT)
285 		width = printf("%-*.*s", width, width, string);
286 	else
287 		width = printf("%*.*s", width, width, string);
288 	return width;
289 }
290 
291 static int
output_string_key(char * string,int width,struct format_key * key)292 output_string_key(char *string, int width, struct format_key *key)
293 {
294 	if (strlen(string) == 0) {
295 		char *p = format_key_lookup(key, "empty");
296 		if (p)
297 			string = p;
298 	}
299 	return output_string(string, width, key_align(key));
300 }
301 
302 static int
output_tab(int column,int tabstop)303 output_tab(int column, int tabstop)
304 {
305 	int goal = (((column + TAB_SIZE - 1) / TAB_SIZE) + tabstop) * TAB_SIZE;
306 	for (;column < goal; column++)
307 		putchar(' ');
308 	return column;
309 }
310 
311 /*FIXME: ignores key */
312 static int
output_duration(time_t t,int width,struct format_key * key)313 output_duration(time_t t, int width, struct format_key *key)
314 {
315         unsigned d,h,m,s;
316 	unsigned outbytes;
317 	char dbuf[INT_BUFSIZE_BOUND(unsigned)+1];
318 	char *dptr = NULL;
319 	unsigned fullwidth, dlen;
320 
321 	d = t / 86400;
322 	t %= 86400;
323 
324 	s = t % 60;
325 	m = t / 60;
326 	if (m > 59) {
327 		h = m / 60;
328 		m -= h*60;
329 	} else
330 		h = 0;
331 
332 	fullwidth = 8;
333 	if (d) {
334 		dptr = uinttostr(d, dbuf);
335 		dlen = strlen(dptr);
336 		fullwidth += dlen + 1;
337 	}
338 
339 	if (d) {
340 		if (width >= fullwidth)
341 			outbytes = printf("%*s+%02u:%02u:%02u",
342 					  width - fullwidth, dptr, h, m, s);
343 		else if (width >= fullwidth - 3)
344 			outbytes = printf("%*sd%02uh%02u",
345 					  width - (dlen + 5),
346 					   dptr, h, m);
347 		else if (width >= fullwidth - 5)
348 			outbytes = printf("%*sd%02uh",
349 					  width - (dlen + 3),
350 					  dptr, h);
351 		else if (width >= dlen + 1)
352 			outbytes = printf("%*sd",
353 					  width - 1, dptr);
354 		else {
355 			outbytes = width;
356 			while (width--)
357 				putchar('>');
358 		}
359 	} else {
360 		if (width >= 8)
361 			outbytes = printf("%*s%02u:%02u:%02u",
362 					  width - 8, "", h, m, s);
363 		else if (width >= 5) {
364 			if (h)
365 				outbytes = printf("%*s%02uh%02u",
366 						  width - 5, "", h, m);
367 			else
368 				outbytes = printf("%*s%02u:%02u",
369 						  width - 5, "", m, s);
370 		} else if (h) {
371 			dptr = uinttostr(h, dbuf);
372 			dlen = strlen(dptr);
373 			if (width >= dlen + 1)
374 				outbytes = printf("%*sh",
375 						  width - 1, dptr);
376 			else {
377 				outbytes = width;
378 				while (width--)
379 					putchar('>');
380 			}
381 		} else {
382 			dptr = uinttostr(s, dbuf);
383 			dlen = strlen(dptr);
384 			if (width >= dlen)
385 				outbytes = printf("%*s", width, dptr);
386 			else {
387 				dptr = uinttostr(m, dbuf);
388 				dlen = strlen(dptr);
389 				if (width >= dlen + 1)
390 					outbytes = printf("%*sm",
391 							  width - 1, dptr);
392 				else {
393 					outbytes = width;
394 					while (width--)
395 						putchar('>');
396 				}
397 			}
398 		}
399 	}
400 
401 	return outbytes;
402 }
403 
404 static int
output_time(struct timeval * tv,int width,struct format_key * key)405 output_time(struct timeval *tv, int width, struct format_key *key)
406 {
407 	struct tm *tm = localtime(&tv->tv_sec);
408 	char *fmt = format_key_lookup(key, "format");
409 
410 	return fprintftime(stdout, fmt ? fmt : rushdb_date_format,
411 			   tm, 0, tv->tv_usec * 1000);
412 }
413 
414 
415 
416 /* Runtime */
417 static int
format_user(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)418 format_user(int outbytes, int width, struct format_key *key,
419 	    struct rush_wtmp *wtmp)
420 {
421 	return output_string_key(wtmp->user, width, key);
422 }
423 
424 static int
format_rule(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)425 format_rule(int outbytes, int width, struct format_key *key,
426 	    struct rush_wtmp *wtmp)
427 {
428 	return output_string_key(wtmp->rule, width, key);
429 }
430 
431 static int
format_command(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)432 format_command(int outbytes, int width, struct format_key *key,
433 	       struct rush_wtmp *wtmp)
434 {
435 	return output_string_key(wtmp->command, width, key);
436 }
437 
438 static int
format_pid(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)439 format_pid(int outbytes, int width, struct format_key *key,
440 	   struct rush_wtmp *wtmp)
441 {
442 	char buf[INT_BUFSIZE_BOUND(uintmax_t)];
443 	return output_string_key(umaxtostr(wtmp->pid, buf), width, key);
444 }
445 
446 static int
format_duration(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)447 format_duration(int outbytes, int width, struct format_key *key,
448 		struct rush_wtmp *wtmp)
449 {
450 	time_t end = wtmp->stop.tv_sec;
451 	time_t x = (end ? end : time(NULL)) - wtmp->start.tv_sec;
452 
453 	return output_duration(x, width, key);
454 }
455 
456 static int
format_start(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)457 format_start(int outbytes, int width, struct format_key *key,
458 	     struct rush_wtmp *wtmp)
459 {
460 	return output_time(&wtmp->start, width, key);
461 }
462 
463 static int
format_stop(int outbytes,int width,struct format_key * key,struct rush_wtmp * wtmp)464 format_stop(int outbytes, int width, struct format_key *key,
465 	    struct rush_wtmp *wtmp)
466 {
467 	if (wtmp->stop.tv_sec == 0 && wtmp->stop.tv_usec == 0)
468 		return output_string_key("running", width, key);
469 	else
470 		return output_time(&wtmp->stop, width, key);
471 }
472 
473 struct format_tab {
474 	char *name;
475 	rushdb_format_fp fun;
476 };
477 
478 static struct format_tab handlers[] = {
479 	{ "user", format_user },
480 	{ "rule", format_rule },
481 	{ "command", format_command },
482 	{ "pid", format_pid },
483 	{ "duration", format_duration },
484 	{ "time", format_start },
485 	{ "start-time", format_start },
486 	{ "stop-time", format_stop },
487 	{ NULL }
488 };
489 
490 static rushdb_format_fp
_lookup(char * name)491 _lookup(char *name)
492 {
493 	int i;
494 	for (i = 0; handlers[i].name; i++)
495 		if (strcmp(handlers[i].name, name) == 0)
496 			return handlers[i].fun;
497 	return NULL;
498 }
499 
500 
501 
502 static slist_t slist;
503 
504 static char *
collect_sequence(char * fmt,int (* cond)(void *,char *),void * closure)505 collect_sequence(char *fmt, int (*cond)(void *, char *), void *closure)
506 {
507 	char c;
508 	char *p;
509 
510 	for (p = fmt; *p && (*cond)(closure, p) == 0; p++) {
511 		if (*p == '\\') {
512 			switch (*++p) {
513 			case 'a':
514 				c = '\a';
515 				break;
516 
517 			case 'b':
518 				c = '\b';
519 				break;
520 
521 			case 'e':
522 				c = '\033';
523 				break;
524 
525 			case 'f':
526 				c = '\f';
527 				break;
528 
529 			case 'n':
530 				c = '\n';
531 				break;
532 
533 			case 't':
534 				c = '\t';
535 				break;
536 
537 			case 'r':
538 				c = '\r';
539 				break;
540 
541 			case 'v':
542 				c = '\v';
543 				break;
544 
545 			case '\n':
546 				continue;
547 
548 			default:
549 				 c = *p;
550 			}
551 			slist_append(slist, &c, 1);
552 		} else if (*p == '\n')
553 			;
554 		else
555 			slist_append(slist, p, 1);
556 	}
557 	return p;
558 }
559 
560 static char *
parse_string_fmt(char * fmt,rushdb_format_t form,int (* cond)(void *,char *),void * closure)561 parse_string_fmt(char *fmt, rushdb_format_t form,
562 		  int (*cond)(void *, char *), void *closure)
563 {
564 	char c;
565 	char *endp = collect_sequence(fmt, cond, closure);
566 
567 	c = 0;
568 	slist_append(slist, &c, 1);
569 	slist_reduce(slist, &form->v.string, NULL);
570 	form->type = FDATA_STRING;
571 	return endp;
572 }
573 
574 static int
_is_closing_quote(void * closure,char * p)575 _is_closing_quote(void *closure, char *p)
576 {
577 	return *(char*)closure == *p;
578 }
579 
580 static int
parse_quote(char ** fmtp,struct rushdb_format * form)581 parse_quote(char **fmtp, struct rushdb_format *form)
582 {
583 	char *p;
584 	p = parse_string_fmt(*fmtp + 1, form, _is_closing_quote, *fmtp);
585 	if (!*p) {
586 		format_error(_("missing closing quote in string started "
587 			       "near `%s'"),
588 			     *fmtp);
589 		return 1;
590 	}
591 	*fmtp = p + 1;
592 	return 0;
593 }
594 
595 static int
_is_open_brace(void * closure,char * p)596 _is_open_brace(void *closure, char *p)
597 {
598 	return *p == '(';
599 }
600 
601 static int
parse_string(char ** fmtp,struct rushdb_format * form)602 parse_string(char **fmtp, struct rushdb_format *form)
603 {
604 	char *p;
605 	p = parse_string_fmt(*fmtp, form, _is_open_brace, NULL);
606 	*fmtp = p;
607 	return 0;
608 }
609 
610 static int
_is_delim(void * closure,char * p)611 _is_delim(void *closure, char *p)
612 {
613 	return c_isspace(*p) || *p == ')';
614 }
615 
616 static char *
get_token(char ** fmtp)617 get_token(char **fmtp)
618 {
619 	char *p;
620 	char c;
621 
622 	while (**fmtp && c_isspace(**fmtp))
623 		++*fmtp;
624 	p = *fmtp;
625 	if (*p == ')') {
626 		slist_append(slist, p, 1);
627 		++*fmtp;
628 	} else {
629 		if (**fmtp == '"' || **fmtp == '\'') {
630 			p = collect_sequence(*fmtp + 1,
631 					     _is_closing_quote, *fmtp);
632 			if (*p == **fmtp)
633 				p++;
634 			*fmtp = p;
635 		} else
636 			*fmtp = collect_sequence(*fmtp, _is_delim, NULL);
637 	}
638 	c = 0;
639 	slist_append(slist, &c, 1);
640 	return slist_reduce(slist, &p, NULL);
641 }
642 
643 static int
is_time_function(rushdb_format_fp fh)644 is_time_function(rushdb_format_fp fh)
645 {
646 	return fh == format_start || fh == format_stop;
647 }
648 
649 static int
time_width(struct rushdb_format * form)650 time_width(struct rushdb_format *form)
651 {
652 	time_t t = 0;
653 	struct tm *tm = localtime(&t);
654 	char *fmt = format_key_lookup(form->key, "format");
655 
656 	return nstrftime(NULL, -1, fmt ? fmt : rushdb_date_format,
657 			 tm, 0, 0);
658 }
659 
660 static int
parse_form(char ** fmtp,struct rushdb_format * form)661 parse_form(char **fmtp, struct rushdb_format *form)
662 {
663 	char *formname, *p;
664 	struct format_key *key_head, *key_tail;
665 
666 	++*fmtp;
667 
668 	formname = get_token(fmtp);
669 	if (strcmp(formname, "newline") == 0) {
670 		form->type = FDATA_NEWLINE;
671 		p = get_token(fmtp);
672 		if (p[0] != ')') {
673 			form->v.nl = strtol(p, NULL, 0);
674 			p = get_token(fmtp);
675 		} else
676 			form->v.nl = 1;
677 	} else if (strcmp(formname, "tab") == 0) {
678 		form->type = FDATA_TAB;
679 		p = get_token(fmtp);
680 		if (p[0] != ')') {
681 			form->v.tabstop = strtol(p, NULL, 0);
682 			p = get_token(fmtp);
683 		} else
684 			form->v.tabstop = 1;
685 	} else {
686 		rushdb_format_fp fh;
687 		int arg;
688 
689 		fh = _lookup(formname);
690 		if (!fh) {
691 			format_error("error in format spec: unknown format %s",
692 				     formname);
693 			return 1;
694 		}
695 
696 		form->type = FDATA_FH;
697 		form->v.fh.fun = fh;
698 
699 		/* Collect optional arguments */
700 		arg = 0;
701 		while ((p = get_token(fmtp)) != NULL &&
702 		       !(p[0] == ':' || p[0] == ')')) {
703 			arg++;
704 			switch (arg) {
705 			case 1: /* width */
706 				form->v.fh.width = strtol(p, NULL, 0);
707 				break;
708 			case 2: /* header */
709 				form->v.fh.header = xstrdup(p);
710 				break;
711 			default:
712 				format_error("wrong number of arguments "
713 					     "to form %s",
714 					     formname);
715 				return 1;
716 			}
717 		}
718 
719 		/* Collect keyword arguments */
720 		key_head = NULL;
721 		while (p && p[0] == ':') {
722 			struct format_key *key = xzalloc(sizeof(*key));
723 			if (!key_head)
724 				key_head = key;
725 			else
726 				key_tail->next = key;
727 			key_tail = key;
728 			key->name = xstrdup(p + 1);
729 			p = get_token(fmtp);
730 			if (p[0] == ')' || p[0] == ':')
731 				key->value = xstrdup("t");
732 			else {
733 				key->value = xstrdup(p);
734 				p = get_token(fmtp);
735 			}
736 		}
737 		form->key = key_head;
738 
739 		if (is_time_function(form->v.fh.fun))
740 			form->v.fh.width = time_width(form);
741 	}
742 
743 	if (p[0] != ')') {
744 		format_error("form `%s' not closed", formname);
745 		return 1;
746 	}
747 	return 0;
748 }
749 
750 
751 rushdb_format_t
rushdb_compile_format(char * fmt)752 rushdb_compile_format(char *fmt)
753 {
754 	struct rushdb_format *form_head = NULL, *form_tail;
755 
756 	slist = slist_create();
757 
758 	while (*fmt) {
759 		int rc;
760 		struct rushdb_format *form = xzalloc(sizeof(*form));
761 		if (!form_head)
762 			form_head = form;
763 		else
764 			form_tail->next = form;
765 		form_tail = form;
766 
767 		if (*fmt == '(')
768 			rc = parse_form(&fmt, form);
769 		else if (*fmt == '"' || *fmt == '\'')
770 			rc = parse_quote(&fmt, form);
771 		else
772 			rc = parse_string(&fmt, form);
773 
774 		if (rc) {
775 			form_free(form_head);
776 			form_head = NULL;
777 			break;
778 		}
779 	}
780 
781 	slist_free(slist);
782 
783 	return form_head;
784 }
785 
786 int
rushdb_print(rushdb_format_t form,struct rush_wtmp * wtmp,int newline)787 rushdb_print(rushdb_format_t form, struct rush_wtmp *wtmp, int newline)
788 {
789 	int i;
790 	int outbytes = 0;
791 
792 	for (; form; form = form->next) {
793 		switch (form->type) {
794 		case FDATA_FH:
795 			outbytes += form->v.fh.fun(outbytes,
796 						   form->v.fh.width,
797 						   form->key,
798 						   wtmp);
799 			break;
800 
801 		case FDATA_STRING:
802 			outbytes += output_string(form->v.string, 0,
803 						  ALIGN_LEFT);
804 			break;
805 
806 		case FDATA_TAB:
807 			outbytes += output_tab(outbytes, form->v.tabstop);
808 			break;
809 
810 		case FDATA_NEWLINE:
811 			for (i = 0; i < form->v.nl; i++)
812 				putchar('\n');
813 			break;
814 
815 		default:
816 			abort();
817 		}
818 	}
819 	if (newline)
820 		putchar('\n');
821 	return outbytes;
822 }
823 
824 void
rushdb_print_header(rushdb_format_t form)825 rushdb_print_header(rushdb_format_t form)
826 {
827 	int i, outbytes = 0;
828 	rushdb_format_t p;
829 
830 	for (p = form; p; p = p->next)
831 		if (p->type == FDATA_NEWLINE)
832 			return;
833 
834 	for (; form; form = form->next) {
835 		switch (form->type) {
836 		case FDATA_FH:
837 			if (form->v.fh.header)
838 				outbytes += output_string(form->v.fh.header,
839 							  form->v.fh.width,
840 							  ALIGN_LEFT);
841 			else
842 				outbytes += output_string("", form->v.fh.width,
843 							  ALIGN_LEFT);
844 			break;
845 
846 		case FDATA_STRING:
847 			outbytes += output_string(form->v.string,
848 						  strlen(form->v.string),
849 						  ALIGN_LEFT);
850 			break;
851 
852 		case FDATA_TAB:
853 			outbytes += output_tab(outbytes, form->v.tabstop);
854 			break;
855 
856 		case FDATA_NEWLINE:
857 			for (i = 0; i < form->v.nl; i++)
858 				putchar('\n');
859 			break;
860 
861 		default:
862 			abort();
863 		}
864 	}
865 	putchar('\n');
866 }
867 
868 int
rushdb_start(struct rush_wtmp * wtmp)869 rushdb_start(struct rush_wtmp *wtmp)
870 {
871 	int status;
872 	enum rushdb_result result;
873 	int rc;
874 
875 	rush_utmp_lock_all(RUSH_LOCK_WRITE);
876 	result = rush_utmp_read(RUSH_STATUS_MAP_BIT(RUSH_STATUS_AVAIL),
877 				&status, NULL);
878 	if (result == rushdb_result_fail)
879 		rc = 1;
880 	else {
881 		gettimeofday(&wtmp->start, NULL);
882 		memset(&wtmp->stop, 0, sizeof(wtmp->stop));
883 		rc = rush_utmp_write(wtmp);
884 	}
885 	rush_utmp_unlock_all();
886 	return rc;
887 }
888 
889 int
rushdb_stop()890 rushdb_stop()
891 {
892 	struct timeval tv;
893 	if (rush_utmp_chstatus(RUSH_STATUS_AVAIL))
894 		return 1;
895 	gettimeofday(&tv, NULL);
896 	return rush_wtmp_update(&tv);
897 }
898 
899 
900