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