1 /* vinum.c: vinum interface program */
3 * Copyright (c) 1997, 1998
4 * Nan Yang Computer Services Limited. All rights reserved.
6 * Written by Greg Lehey
8 * This software is distributed under the so-called ``Berkeley
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Nan Yang Computer
23 * 4. Neither the name of the Company nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * This software is provided ``as is'', and any express or implied
28 * warranties, including, but not limited to, the implied warranties of
29 * merchantability and fitness for a particular purpose are disclaimed.
30 * In no event shall the company or contributors be liable for any
31 * direct, indirect, incidental, special, exemplary, or consequential
32 * damages (including, but not limited to, procurement of substitute
33 * goods or services; loss of use, data, or profits; or business
34 * interruption) however caused and on any theory of liability, whether
35 * in contract, strict liability, or tort (including negligence or
36 * otherwise) arising in any way out of the use of this software, even if
37 * advised of the possibility of such damage.
39 * $Id: v.c,v 1.31 2000/09/03 01:29:26 grog Exp grog $
40 * $FreeBSD: src/sbin/vinum/v.c,v 1.26.2.3 2001/03/13 03:04:06 grog Exp $
41 * $DragonFly: src/sbin/vinum/v.c,v 1.4 2005/11/06 12:51:40 swildner Exp $
56 #include <sys/ioctl.h>
57 #include <dev/raid/vinum/vinumhdr.h>
59 #include <sys/types.h>
61 #include <readline/history.h>
62 #include <readline/readline.h>
63 #include <sys/linker.h>
64 #include <sys/module.h>
65 #include <sys/resource.h>
67 FILE *cf; /* config file handle */
68 FILE *history; /* history file */
69 char *historyfile; /* and its name */
71 char *dateformat; /* format in which to store date */
73 char buffer[BUFSIZE]; /* buffer to read in to */
75 int line = 0; /* stdin line number for error messages */
76 int file_line = 0; /* and line in input file (yes, this is tacky) */
77 int inerror; /* set to 1 to exit after end of config file */
82 int debug = 0; /* debug flag, usage varies */
84 int force = 0; /* set to 1 to force some dangerous ops */
85 int interval = 0; /* interval in ms between init/revive */
86 int vflag = 0; /* set verbose operation or verify */
87 int Verbose = 0; /* set very verbose operation */
88 int recurse = 0; /* set recursion */
89 int sflag = 0; /* show statistics */
90 int SSize = 0; /* sector size for revive */
91 int dowait = 0; /* wait for completion */
92 char *objectname; /* name to be passed for -n flag */
94 /* Structures to read kernel data into */
95 struct _vinum_conf vinum_conf; /* configuration information */
102 jmp_buf command_fail; /* return on a failed command */
103 int superdev; /* vinum super device */
105 void start_daemon(void);
107 #define ofs(x) ((void *) (& ((struct confdata *) 0)->x)) /* offset of x in struct confdata */
109 char *token[MAXARGS]; /* pointers to individual tokens */
110 int tokens; /* number of tokens */
113 main(int argc, char *argv[], char *envp[])
115 struct stat histstat;
117 if (modfind(VINUMMOD) < 0) {
118 /* need to load the vinum module */
119 if (kldload(VINUMMOD) < 0 || modfind(VINUMMOD) < 0) {
120 perror(VINUMMOD ": Kernel module not available");
124 dateformat = getenv("VINUM_DATEFORMAT");
125 if (dateformat == NULL)
126 dateformat = "%e %b %Y %H:%M:%S";
127 historyfile = getenv("VINUM_HISTORY");
128 if (historyfile == NULL)
129 historyfile = DEFAULT_HISTORYFILE;
130 if (stat(historyfile, &histstat) == 0) { /* history file exists */
131 if ((histstat.st_mode & S_IFMT) != S_IFREG) {
133 "Vinum history file %s must be a regular file\n",
137 } else if ((errno != ENOENT) /* not "not there", */
138 &&(errno != EROFS)) { /* and not read-only file system */
140 "Can't open %s: %s (%d)\n",
146 history = fopen(historyfile, "a+");
147 if (history != NULL) {
149 fprintf(history, "*** " VINUMMOD " started ***\n");
150 fflush(history); /* before we start the daemon */
152 superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open vinum superdevice */
153 if (superdev < 0) { /* no go */
154 if (errno == ENODEV) { /* not configured, */
155 superdev = open(VINUM_WRONGSUPERDEV_NAME, O_RDWR); /* do we have a debug mismatch? */
156 if (superdev >= 0) { /* yup! */
159 "This program is compiled with debug support, but the kernel module does\n"
160 "not have debug support. This program must be matched with the kernel\n"
161 "module. Please alter /usr/src/sbin/" VINUMMOD "/Makefile and remove\n"
162 "the option -DVINUMDEBUG from the CFLAGS definition, or alternatively\n"
163 "edit /usr/src/sys/modules/" VINUMMOD "/Makefile and add the option\n"
164 "-DVINUMDEBUG to the CFLAGS definition. Then rebuild the component\n"
165 "of your choice with 'make clean all install'. If you rebuild the kernel\n"
166 "module, you must stop " VINUMMOD " and restart it\n");
169 "This program is compiled without debug support, but the kernel module\n"
170 "includes debug support. This program must be matched with the kernel\n"
171 "module. Please alter /usr/src/sbin/" VINUMMOD "/Makefile and add\n"
172 "the option -DVINUMDEBUG to the CFLAGS definition, or alternatively\n"
173 "edit /usr/src/sys/modules/" VINUMMOD "/Makefile and remove the option\n"
174 "-DVINUMDEBUG from the CFLAGS definition. Then rebuild the component\n"
175 "of your choice with 'make clean all install'. If you rebuild the kernel\n"
176 "module, you must stop " VINUMMOD " and restart it\n");
180 } else if (errno == ENOENT) /* we don't have our node, */
181 make_devices(); /* create them first */
183 perror("Can't open " VINUM_SUPERDEV_NAME);
187 /* Check if the dæmon is running. If not, start it in the
191 if (argc > 1) { /* we have a command on the line */
192 if (setjmp(command_fail) != 0) /* long jumped out */
194 parseline(argc - 1, &argv[1]); /* do it */
197 * Catch a possible race condition which could cause us to
198 * longjmp() into nowhere if we receive a SIGINT in the next few
201 if (setjmp(command_fail)) /* come back here on catastrophic failure */
203 setsigs(); /* set signal handler */
206 int childstatus; /* from wait4 */
208 if (setjmp(command_fail) == 2) /* come back here on catastrophic failure */
209 fprintf(stderr, "*** interrupted ***\n"); /* interrupted */
211 while (wait4(-1, &childstatus, WNOHANG, NULL) > 0); /* wait for all dead children */
212 c = readline(VINUMMOD " -> "); /* get an input */
213 if (c == NULL) { /* EOF or error */
215 fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno);
221 } else if (*c) { /* got something there */
222 add_history(c); /* save it in the history */
223 strcpy(buffer, c); /* put it where we can munge it */
225 line++; /* count the lines */
226 tokens = tokenize(buffer, token);
227 /* got something potentially worth parsing */
229 parseline(tokens, token); /* and do what he says */
235 return 0; /* normal completion */
238 /* stop the hard way */
240 vinum_quit(int argc, char *argv[], char *argv0[])
245 /* Set action on receiving a SIGINT */
249 struct sigaction act;
251 act.sa_handler = catchsig;
253 sigemptyset(&act.sa_mask);
254 sigaction(SIGINT, &act, NULL);
260 longjmp(command_fail, 2);
263 #define FUNKEY(x) { kw_##x, &vinum_##x } /* create pair "kw_foo", vinum_foo */
264 #define vinum_move vinum_mv /* synonym for 'mv' */
268 void (*fun) (int argc, char *argv[], char *arg0[]);
311 FUNKEY(rebuildparity),
315 /* Take args arguments at argv and attempt to perform the operation specified */
317 parseline(int args, char *argv[])
321 enum keyword command; /* command to execute */
323 if (history != NULL) { /* save the command to history file */
325 for (i = 0; i < args; i++) /* all args */
326 fprintf(history, "%s ", argv[i]);
327 fputs("\n", history);
329 if ((args == 0) /* empty line */
330 ||(*argv[0] == '#')) /* or a comment, */
332 if (args == MAXARGS) { /* too many arguments, */
333 fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]);
336 command = get_keyword(argv[0], &keyword_set);
337 dowait = 0; /* initialize flags */
338 force = 0; /* initialize flags */
339 vflag = 0; /* initialize flags */
340 Verbose = 0; /* initialize flags */
341 recurse = 0; /* initialize flags */
342 sflag = 0; /* initialize flags */
343 objectname = NULL; /* no name yet */
346 * first handle generic options
347 * We don't use getopt(3) because
348 * getopt doesn't allow merging flags
349 * (for example, -fr).
351 for (i = 1; (i < args) && (argv[i][0] == '-'); i++) { /* while we have flags */
352 for (j = 1; j < strlen(argv[i]); j++)
353 switch (argv[i][j]) {
355 case 'd': /* -d: debug */
360 case 'f': /* -f: force */
364 case 'i': /* interval */
366 if (argv[i][j + 1] != '\0') /* operand follows, */
367 interval = atoi(&argv[i][j + 1]); /* use it */
368 else if (args > (i + 1)) /* another following, */
369 interval = atoi(argv[++i]); /* use it */
370 if (interval == 0) /* nothing valid, */
371 fprintf(stderr, "-i: no interval specified\n");
374 case 'n': /* -n: get name */
375 if (i == args - 1) { /* last arg */
376 fprintf(stderr, "-n requires a name parameter\n");
379 objectname = argv[++i]; /* pick it up */
380 j = strlen(argv[i]); /* skip the next parm */
383 case 'r': /* -r: recurse */
387 case 's': /* -s: show statistics */
393 if (argv[i][j + 1] != '\0') /* operand follows, */
394 SSize = atoi(&argv[i][j + 1]); /* use it */
395 else if (args > (i + 1)) /* another following, */
396 SSize = atoi(argv[++i]); /* use it */
397 if (SSize == 0) /* nothing valid, */
398 fprintf(stderr, "-S: no size specified\n");
401 case 'v': /* -v: verbose */
405 case 'V': /* -V: Very verbose */
410 case 'w': /* -w: wait for completion */
415 fprintf(stderr, "Invalid flag: %s\n", argv[i]);
419 /* Pass what we have left to the command to handle it */
420 for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) {
421 if (funkeys[j].kw == command) { /* found the command */
422 funkeys[j].fun(args - i, &argv[i], &argv[0]);
426 fprintf(stderr, "Unknown command: %s\n", argv[0]);
430 get_drive_info(struct drive *drive, int index)
432 *(int *) drive = index; /* put in drive to hand to driver */
433 if (ioctl(superdev, VINUM_DRIVECONFIG, drive) < 0) {
435 "Can't get config for drive %d: %s\n",
438 longjmp(command_fail, -1);
443 get_sd_info(struct sd *sd, int index)
445 *(int *) sd = index; /* put in sd to hand to driver */
446 if (ioctl(superdev, VINUM_SDCONFIG, sd) < 0) {
448 "Can't get config for subdisk %d: %s\n",
451 longjmp(command_fail, -1);
455 /* Get the contents of the sd entry for subdisk <sdno>
456 * of the specified plex. */
458 get_plex_sd_info(struct sd *sd, int plexno, int sdno)
460 ((int *) sd)[0] = plexno;
461 ((int *) sd)[1] = sdno; /* pass parameters */
462 if (ioctl(superdev, VINUM_PLEXSDCONFIG, sd) < 0) {
464 "Can't get config for subdisk %d (part of plex %d): %s\n",
468 longjmp(command_fail, -1);
473 get_plex_info(struct plex *plex, int index)
475 *(int *) plex = index; /* put in plex to hand to driver */
476 if (ioctl(superdev, VINUM_PLEXCONFIG, plex) < 0) {
478 "Can't get config for plex %d: %s\n",
481 longjmp(command_fail, -1);
486 get_volume_info(struct volume *volume, int index)
488 *(int *) volume = index; /* put in volume to hand to driver */
489 if (ioctl(superdev, VINUM_VOLCONFIG, volume) < 0) {
491 "Can't get config for volume %d: %s\n",
494 longjmp(command_fail, -1);
499 find_drive_by_devname(char *name)
503 if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
504 perror("Can't get vinum config");
507 for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
508 get_drive_info(&drive, driveno);
509 if ((drive.state != drive_unallocated) /* real drive */
510 &&(!strcmp(drive.devicename, name))) /* and the name's right, */
511 return &drive; /* found it */
513 return NULL; /* no drive of that name */
516 /* Create the device nodes for vinum objects */
525 if (access("/dev", W_OK) < 0) { /* can't access /dev to write? */
526 if (errno == EROFS) /* because it's read-only, */
527 fprintf(stderr, VINUMMOD ": /dev is mounted read-only, not rebuilding " VINUM_DIR "\n");
529 perror(VINUMMOD ": Can't write to /dev");
534 fprintf(history, "*** Created devices ***\n");
536 if (superdev >= 0) /* super device open */
539 system("rm -rf " VINUM_DIR); /* remove the old directories */
540 system("mkdir -p " VINUM_DIR "/drive " /* and make them again */
545 if (mknod(VINUM_SUPERDEV_NAME,
546 S_IRUSR | S_IWUSR | S_IFCHR, /* user only */
547 makedev(VINUM_CDEV_MAJOR, VINUM_SUPERDEV)) < 0)
548 fprintf(stderr, "Can't create %s: %s\n", VINUM_SUPERDEV_NAME, strerror(errno));
550 if (mknod(VINUM_WRONGSUPERDEV_NAME,
551 S_IRUSR | S_IWUSR | S_IFCHR, /* user only */
552 makedev(VINUM_CDEV_MAJOR, VINUM_WRONGSUPERDEV)) < 0)
553 fprintf(stderr, "Can't create %s: %s\n", VINUM_WRONGSUPERDEV_NAME, strerror(errno));
555 superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open the super device */
557 if (mknod(VINUM_DAEMON_DEV_NAME, /* daemon super device */
558 S_IRUSR | S_IWUSR | S_IFCHR, /* user only */
559 makedev(VINUM_CDEV_MAJOR, VINUM_DAEMON_DEV)) < 0)
560 fprintf(stderr, "Can't create %s: %s\n", VINUM_DAEMON_DEV_NAME, strerror(errno));
562 if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
563 perror("Can't get vinum config");
566 for (volno = 0; volno < vinum_conf.volumes_allocated; volno++)
567 make_vol_dev(volno, 0);
569 for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++)
570 make_plex_dev(plexno, 0);
572 for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++)
575 /* Drives. Do this later (both logical and physical names) XXX */
576 for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
577 char filename[PATH_MAX]; /* for forming file names */
579 get_drive_info(&drive, driveno);
580 if (drive.state > drive_referenced) {
581 sprintf(filename, "ln -s %s " VINUM_DIR "/drive/%s", drive.devicename, drive.label.name);
587 /* make the devices for a volume */
589 make_vol_dev(int volno, int recurse)
592 char filename[PATH_MAX]; /* for forming file names */
595 get_volume_info(&vol, volno);
596 if (vol.state != volume_unallocated) { /* we could have holes in our lists */
597 voldev = VINUMDEV(volno, 0, 0, VINUM_VOLUME_TYPE); /* create a device number */
599 /* Create /dev/vinum/<myvol> */
600 sprintf(filename, VINUM_DIR "/%s", vol.name);
601 if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
602 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
604 /* Create /dev/vinum/vol/<myvol> */
605 sprintf(filename, VINUM_DIR "/vol/%s", vol.name);
606 if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
607 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
609 if (vol.plexes > 0) {
610 /* Create /dev/vinum/vol/<myvol>.plex/ */
611 sprintf(filename, VINUM_DIR "/vol/%s.plex", vol.name);
612 if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
613 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
616 for (plexno = 0; plexno < vol.plexes; plexno++)
617 make_plex_dev(plex.plexno, recurse);
622 * Create device entries for the plexes in
623 * /dev/vinum/<vol>.plex/ and /dev/vinum/plex.
626 make_plex_dev(int plexno, int recurse)
628 dev_t plexdev; /* device */
629 char filename[PATH_MAX]; /* for forming file names */
632 get_plex_info(&plex, plexno);
633 if (plex.state != plex_unallocated) {
634 plexdev = VINUM_PLEX(plexno);
636 /* /dev/vinum/plex/<plex> */
637 sprintf(filename, VINUM_DIR "/plex/%s", plex.name);
638 if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
639 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
641 if (plex.volno >= 0) {
642 get_volume_info(&vol, plex.volno);
643 plexdev = VINUMDEV(plex.volno, plexno, 0, VINUM_PLEX_TYPE);
645 /* Create device /dev/vinum/vol/<vol>.plex/<plex> */
646 sprintf(filename, VINUM_DIR "/vol/%s.plex/%s", vol.name, plex.name);
647 if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
648 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
650 /* Create directory /dev/vinum/vol/<vol>.plex/<plex>.sd */
651 sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd", vol.name, plex.name);
652 if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
653 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
656 for (sdno = 0; sdno < plex.subdisks; sdno++) {
657 get_plex_sd_info(&sd, plex.plexno, sdno);
658 make_sd_dev(sd.sdno);
664 /* Create the contents of /dev/vinum/sd and /dev/vinum/rsd */
666 make_sd_dev(int sdno)
668 dev_t sddev; /* device */
669 char filename[PATH_MAX]; /* for forming file names */
671 get_sd_info(&sd, sdno);
672 if (sd.state != sd_unallocated) {
673 sddev = VINUM_SD(sdno);
675 /* /dev/vinum/sd/<sd> */
676 sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
677 if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, sddev) < 0)
678 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
683 /* command line interface for the 'makedev' command */
685 vinum_makedev(int argc, char *argv[], char *arg0[])
691 * Find the object "name". Return object type at type,
692 * and the index as the return value.
693 * If not found, return -1 and invalid_object.
696 find_object(const char *name, enum objecttype *type)
700 if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
701 perror("Can't get vinum config");
702 *type = invalid_object;
705 /* Search the drive table */
706 for (object = 0; object < vinum_conf.drives_allocated; object++) {
707 get_drive_info(&drive, object);
708 if (strcmp(name, drive.label.name) == 0) {
709 *type = drive_object;
714 /* Search the subdisk table */
715 for (object = 0; object < vinum_conf.subdisks_allocated; object++) {
716 get_sd_info(&sd, object);
717 if (strcmp(name, sd.name) == 0) {
723 /* Search the plex table */
724 for (object = 0; object < vinum_conf.plexes_allocated; object++) {
725 get_plex_info(&plex, object);
726 if (strcmp(name, plex.name) == 0) {
732 /* Search the volume table */
733 for (object = 0; object < vinum_conf.volumes_allocated; object++) {
734 get_volume_info(&vol, object);
735 if (strcmp(name, vol.name) == 0) {
736 *type = volume_object;
741 /* Didn't find the name: invalid */
742 *type = invalid_object;
746 /* Continue reviving a subdisk in the background */
748 continue_revive(int sdno)
752 get_sd_info(&sd, sdno);
755 pid = fork(); /* do this in the background */
758 if (pid == 0) { /* we're the child */
759 struct _ioctl_reply reply;
760 struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
762 openlog(VINUMMOD, LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
763 syslog(LOG_INFO | LOG_KERN, "reviving %s", sd.name);
764 setproctitle("reviving %s", sd.name);
766 for (reply.error = EAGAIN; reply.error == EAGAIN;) { /* revive the subdisk */
768 usleep(interval * 1000); /* pause between each copy */
769 message->index = sdno; /* pass sd number */
770 message->type = sd_object; /* and type of object */
771 message->state = object_up;
772 if (SSize != 0) { /* specified a size for init */
774 SSize <<= DEV_BSHIFT;
775 message->blocksize = SSize;
777 message->blocksize = DEFAULT_REVIVE_BLOCKSIZE;
778 ioctl(superdev, VINUM_SETSTATE, message);
781 syslog(LOG_ERR | LOG_KERN,
782 "can't revive %s: %s",
784 reply.msg[0] ? reply.msg : strerror(reply.error));
788 get_sd_info(&sd, sdno); /* update the info */
789 syslog(LOG_INFO | LOG_KERN, "%s is %s", sd.name, sd_state(sd.state));
793 } else if (pid < 0) /* couldn't fork? */
794 fprintf(stderr, "Can't continue reviving %s: %s\n", sd.name, strerror(errno));
796 printf("Reviving %s in the background\n", sd.name);
800 * Check if the daemon is running,
801 * start it if it isn't. The check itself
802 * could take a while, so we do it as a separate
803 * process, which will become the daemon if one isn't
815 if (pid == 0) { /* We're the child, do the work */
817 * We have a problem when stopping the subsystem:
818 * The only way to know that we're idle is when
819 * all open superdevs close. But we want the
820 * daemon to clean up for us, and since we can't
821 * count the opens, we need to have the main device
822 * closed when we stop. We solve this conundrum
823 * by getting the daemon to open a separate device.
825 close(superdev); /* this is the wrong device */
826 superdev = open(VINUM_DAEMON_DEV_NAME, O_RDWR); /* open deamon superdevice */
828 perror("Can't open " VINUM_DAEMON_DEV_NAME);
831 error = daemon(0, 0); /* this will fork again, but who's counting? */
833 fprintf(stderr, "Can't start daemon: %s (%d)\n", strerror(errno), errno);
836 setproctitle(VINUMMOD " daemon"); /* show what we're doing */
837 status = ioctl(superdev, VINUM_FINDDAEMON, NULL);
838 if (status != 0) { /* no daemon, */
839 ioctl(superdev, VINUM_DAEMON, &vflag); /* we should hang here */
840 syslog(LOG_ERR | LOG_KERN, "%s", strerror(errno));
843 exit(0); /* when told to die */
844 } else if (pid < 0) /* couldn't fork */
845 printf("Can't fork to check daemon\n");
853 char datetext[MAXDATETEXT];
856 if (history != NULL) {
857 if (gettimeofday(&now, NULL) != 0) {
858 fprintf(stderr, "Can't get time: %s\n", strerror(errno));
862 date = localtime(&sec);
863 strftime(datetext, MAXDATETEXT, dateformat, date),