[9] | 1 | /* last - display login history Author: Terrence W. Holm */
|
---|
| 2 |
|
---|
| 3 | /* last- Display the user log-in history.
|
---|
| 4 | * Last(1) searches backwards through the file of log-in
|
---|
| 5 | * records (/usr/adm/wtmp), displaying the length of
|
---|
| 6 | * log-in sessions as requested by the options:
|
---|
| 7 | *
|
---|
| 8 | * Usage: last [-r] [-count] [-f file] [name] [tty] ...
|
---|
| 9 | *
|
---|
| 10 | * -r Search backwards only until the last reboot
|
---|
| 11 | * record.
|
---|
| 12 | *
|
---|
| 13 | * -count Only print out <count> records. Last(1) stops
|
---|
| 14 | * when either -r or -count is satisfied, or at
|
---|
| 15 | * the end of the file if neither is given.
|
---|
| 16 | *
|
---|
| 17 | * -f file Use "file" instead of "/usr/adm/wtmp".
|
---|
| 18 | *
|
---|
| 19 | * name Print records for the user "name".
|
---|
| 20 | *
|
---|
| 21 | * tty Print records for the terminal "tty". Actually,
|
---|
| 22 | * a list of names may be given and all records
|
---|
| 23 | * that match either the user or tty name are
|
---|
| 24 | * printed. If no names are given then all records
|
---|
| 25 | * are displayed.
|
---|
| 26 | *
|
---|
| 27 | * A sigquit (^\) causes last(1) to display how far it
|
---|
| 28 | * has gone back in the log-in record file, it then
|
---|
| 29 | * continues. This is used to check on the progress of
|
---|
| 30 | * long running searches. A sigint will stop last(1).
|
---|
| 31 | *
|
---|
| 32 | * Author: Terrence W. Holm May 1988
|
---|
| 33 | *
|
---|
| 34 | * Revision:
|
---|
| 35 | * Fred van Kempen, October 1989
|
---|
| 36 | * -Adapted to MSS.
|
---|
| 37 | * -Adapted to new utmp database.
|
---|
| 38 | *
|
---|
| 39 | * Fred van Kempen, December 1989
|
---|
| 40 | * -Adapted to POSIX (MINIX 1.5)
|
---|
| 41 | *
|
---|
| 42 | * Fred van Kempen, January 1990
|
---|
| 43 | * -Final edit for 1.5
|
---|
| 44 | *
|
---|
| 45 | * Philip Homburg, March 1992
|
---|
| 46 | * -Include host in output
|
---|
| 47 | *
|
---|
| 48 | * Kees J. Bot, July 1997
|
---|
| 49 | * -Approximate system uptime from last reboot record
|
---|
| 50 | */
|
---|
| 51 | #include <sys/types.h>
|
---|
| 52 | #include <signal.h>
|
---|
| 53 | #include <string.h>
|
---|
| 54 | #include <utmp.h>
|
---|
| 55 | #include <time.h>
|
---|
| 56 | #include <stdlib.h>
|
---|
| 57 | #include <stdio.h>
|
---|
| 58 | #include <errno.h>
|
---|
| 59 |
|
---|
| 60 | #include <minix/paths.h>
|
---|
| 61 |
|
---|
| 62 | #define FALSE 0
|
---|
| 63 | #define TRUE 1
|
---|
| 64 | #define RLOGIN 1
|
---|
| 65 |
|
---|
| 66 | #define BUFFER_SIZE 4096 /* Room for wtmp records */
|
---|
| 67 | #define MAX_WTMP_COUNT ( BUFFER_SIZE / sizeof(struct utmp) )
|
---|
| 68 |
|
---|
| 69 | #define min( a, b ) ( (a < b) ? a : b )
|
---|
| 70 | #define max( a, b ) ( (a > b) ? a : b )
|
---|
| 71 |
|
---|
| 72 |
|
---|
| 73 | typedef struct logout { /* A logout time record */
|
---|
| 74 | char line[12]; /* The terminal name */
|
---|
| 75 | long time; /* The logout time */
|
---|
| 76 | struct logout *next; /* Next in linked list */
|
---|
| 77 | } logout;
|
---|
| 78 |
|
---|
| 79 |
|
---|
| 80 | static char *Version = "@(#) LAST 1.7 (10/24/92)";
|
---|
| 81 |
|
---|
| 82 |
|
---|
| 83 | /* command-line option flags */
|
---|
| 84 | char boot_limit = FALSE; /* stop on latest reboot */
|
---|
| 85 | char count_limit = FALSE; /* stop after print_count */
|
---|
| 86 | char tell_uptime = FALSE; /* tell uptime since last reboot */
|
---|
| 87 | int print_count;
|
---|
| 88 | char *prog; /* name of this program */
|
---|
| 89 | int arg_count; /* used to select specific */
|
---|
| 90 | char **args; /* users and ttys */
|
---|
| 91 |
|
---|
| 92 | /* global variables */
|
---|
| 93 | long boot_time = 0; /* Zero means no reboot yet */
|
---|
| 94 | char *boot_down; /* "crash" or "down " flag */
|
---|
| 95 | logout *first_link = NULL; /* List of logout times */
|
---|
| 96 | int interrupt = FALSE; /* If sigint or sigquit occurs */
|
---|
| 97 |
|
---|
| 98 | _PROTOTYPE(int main, (int argc, char **argv));
|
---|
| 99 | _PROTOTYPE(void Sigint, (int sig));
|
---|
| 100 | _PROTOTYPE(void Sigquit, (int sig));
|
---|
| 101 | _PROTOTYPE(void usage, (void));
|
---|
| 102 | _PROTOTYPE(void Process, (struct utmp *wtmp));
|
---|
| 103 | _PROTOTYPE(int Print_Record, (struct utmp *wtmp));
|
---|
| 104 | _PROTOTYPE(void Print_Duration, (long from, long to));
|
---|
| 105 | _PROTOTYPE(void Print_Uptime, (void));
|
---|
| 106 | _PROTOTYPE(void Record_Logout_Time, (struct utmp *wtmp));
|
---|
| 107 |
|
---|
| 108 | /* Sigint() and Sigquit() Flag occurrence of an interrupt. */
|
---|
| 109 | void Sigint(sig)
|
---|
| 110 | int sig;
|
---|
| 111 | {
|
---|
| 112 | interrupt = SIGINT;
|
---|
| 113 | }
|
---|
| 114 |
|
---|
| 115 |
|
---|
| 116 | void Sigquit(sig)
|
---|
| 117 | int sig;
|
---|
| 118 | {
|
---|
| 119 | interrupt = SIGQUIT;
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 |
|
---|
| 123 | void usage()
|
---|
| 124 | {
|
---|
| 125 | fprintf(stderr,
|
---|
| 126 | "Usage: last [-r] [-u] [-count] [-f file] [name] [tty] ...\n");
|
---|
| 127 | exit(-1);
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 |
|
---|
| 131 | /* A log-in record format file contains four types of records.
|
---|
| 132 | *
|
---|
| 133 | * [1] generated on a system reboot:
|
---|
| 134 | *
|
---|
| 135 | * line="~", name="reboot", host="", time=date()
|
---|
| 136 | *
|
---|
| 137 | *
|
---|
| 138 | * [2] generated after a shutdown:
|
---|
| 139 | *
|
---|
| 140 | * line="~", name="shutdown", host="", time=date()
|
---|
| 141 | *
|
---|
| 142 | *
|
---|
| 143 | * [3] generated on a successful login(1)
|
---|
| 144 | *
|
---|
| 145 | * line=ttyname(), name=cuserid(), host=, time=date()
|
---|
| 146 | *
|
---|
| 147 | *
|
---|
| 148 | * [4] generated by init(8) on a logout
|
---|
| 149 | *
|
---|
| 150 | * line=ttyname(), name="", host="", time=date()
|
---|
| 151 | *
|
---|
| 152 | *
|
---|
| 153 | * Note: This version of last(1) does not recognize the '|' and '}' time
|
---|
| 154 | * change records. Last(1) pairs up line login's and logout's to
|
---|
| 155 | * generate four types of output lines:
|
---|
| 156 | *
|
---|
| 157 | * [1] a system reboot or shutdown
|
---|
| 158 | *
|
---|
| 159 | * reboot ~ Mon May 16 14:16
|
---|
| 160 | * shutdown ~ Mon May 16 14:15
|
---|
| 161 | *
|
---|
| 162 | * [2] a login with a matching logout
|
---|
| 163 | *
|
---|
| 164 | * edwin tty1 Thu May 26 20:05 - 20:32 (00:27)
|
---|
| 165 | *
|
---|
| 166 | * [3] a login followed by a reboot or shutdown
|
---|
| 167 | *
|
---|
| 168 | * root tty0 Mon May 16 13:57 - crash (00:19)
|
---|
| 169 | * root tty1 Mon May 16 13:45 - down (00:30)
|
---|
| 170 | *
|
---|
| 171 | * [4] a login not followed by a logout or reboot
|
---|
| 172 | *
|
---|
| 173 | * terry tty0 Thu May 26 21:19 still logged in
|
---|
| 174 | */
|
---|
| 175 | void Process(wtmp)
|
---|
| 176 | struct utmp *wtmp;
|
---|
| 177 | {
|
---|
| 178 | logout *link;
|
---|
| 179 | logout *next_link;
|
---|
| 180 | char is_reboot;
|
---|
| 181 |
|
---|
| 182 | /* suppress the job number on an "ftp" line */
|
---|
| 183 | if (!strncmp(wtmp->ut_line, "ftp", (size_t)3)) strncpy(wtmp->ut_line, "ftp", (size_t)8);
|
---|
| 184 |
|
---|
| 185 | if (!strcmp(wtmp->ut_line, "~")) {
|
---|
| 186 | /* A reboot or shutdown record */
|
---|
| 187 | if (boot_limit) exit(0);
|
---|
| 188 |
|
---|
| 189 | if (Print_Record(wtmp)) putchar('\n');
|
---|
| 190 | boot_time = wtmp->ut_time;
|
---|
| 191 |
|
---|
| 192 | is_reboot = !strcmp(wtmp->ut_name, "reboot");
|
---|
| 193 | if (is_reboot)
|
---|
| 194 | boot_down = "crash";
|
---|
| 195 | else
|
---|
| 196 | boot_down = "down ";
|
---|
| 197 |
|
---|
| 198 | if (tell_uptime) {
|
---|
| 199 | if (!is_reboot) {
|
---|
| 200 | fprintf(stderr,
|
---|
| 201 | "%s: no reboot record added to wtmp file on system boot!\n",
|
---|
| 202 | prog);
|
---|
| 203 | exit(1);
|
---|
| 204 | }
|
---|
| 205 | Print_Uptime();
|
---|
| 206 | exit(0);
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | /* remove any logout records */
|
---|
| 210 | for (link = first_link; link != NULL; link = next_link) {
|
---|
| 211 | next_link = link->next;
|
---|
| 212 | free(link);
|
---|
| 213 | }
|
---|
| 214 | first_link = NULL;
|
---|
| 215 | } else if (wtmp->ut_name[0] == '\0') {
|
---|
| 216 | /* A logout record */
|
---|
| 217 | Record_Logout_Time(wtmp);
|
---|
| 218 | } else {
|
---|
| 219 | /* A login record */
|
---|
| 220 | for (link = first_link; link != NULL; link = link->next)
|
---|
| 221 | if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
|
---|
| 222 | /* found corresponding logout record */
|
---|
| 223 | if (Print_Record(wtmp)) {
|
---|
| 224 | printf("- %.5s ", ctime(&link->time) + 11);
|
---|
| 225 | Print_Duration(wtmp->ut_time, link->time);
|
---|
| 226 | }
|
---|
| 227 | /* record login time */
|
---|
| 228 | link->time = wtmp->ut_time;
|
---|
| 229 | return;
|
---|
| 230 | }
|
---|
| 231 | /* could not find a logout record for this login tty */
|
---|
| 232 | if (Print_Record(wtmp))
|
---|
| 233 | if (boot_time == 0) /* still on */
|
---|
| 234 | printf(" still logged in\n");
|
---|
| 235 | else { /* system crashed while on */
|
---|
| 236 | printf("- %s ", boot_down);
|
---|
| 237 | Print_Duration(wtmp->ut_time, boot_time);
|
---|
| 238 | }
|
---|
| 239 | Record_Logout_Time(wtmp); /* Needed in case of 2
|
---|
| 240 | * consecutive logins */
|
---|
| 241 | }
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 |
|
---|
| 245 | /* Print_Record(wtmp) If the record was requested, then print out
|
---|
| 246 | * the user name, terminal, host and time.
|
---|
| 247 | */
|
---|
| 248 | int Print_Record(wtmp)
|
---|
| 249 | struct utmp *wtmp;
|
---|
| 250 | {
|
---|
| 251 | int i;
|
---|
| 252 | char print_flag = FALSE;
|
---|
| 253 |
|
---|
| 254 | /* just interested in the uptime? */
|
---|
| 255 | if (tell_uptime) return(FALSE);
|
---|
| 256 |
|
---|
| 257 | /* check if we have already printed the requested number of records */
|
---|
| 258 | if (count_limit && print_count == 0) exit(0);
|
---|
| 259 |
|
---|
| 260 | for (i = 0; i < arg_count; ++i)
|
---|
| 261 | if (!strncmp(args[i], wtmp->ut_name, sizeof(wtmp->ut_name)) ||
|
---|
| 262 | !strncmp(args[i], wtmp->ut_line, sizeof(wtmp->ut_line)))
|
---|
| 263 | print_flag = TRUE;
|
---|
| 264 |
|
---|
| 265 | if (arg_count == 0 || print_flag) {
|
---|
| 266 | #ifdef RLOGIN
|
---|
| 267 | printf("%-8.8s %-8.8s %-16.16s %.16s ",
|
---|
| 268 | wtmp->ut_name, wtmp->ut_line, wtmp->ut_host,
|
---|
| 269 | ctime(&wtmp->ut_time));
|
---|
| 270 | #else
|
---|
| 271 | printf("%-8.8s %-8.8s %.16s ",
|
---|
| 272 | wtmp->ut_name, wtmp->ut_line, ctime(&wtmp->ut_time));
|
---|
| 273 | #endif
|
---|
| 274 | --print_count;
|
---|
| 275 | return(TRUE);
|
---|
| 276 | }
|
---|
| 277 | return(FALSE);
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 |
|
---|
| 281 | /* Print_Duration(from, to) Calculate and print the days and hh:mm between
|
---|
| 282 | * the log-in and the log-out.
|
---|
| 283 | */
|
---|
| 284 | void Print_Duration(from, to)
|
---|
| 285 | long from;
|
---|
| 286 | long to;
|
---|
| 287 | {
|
---|
| 288 | long delta, days, hours, minutes;
|
---|
| 289 |
|
---|
| 290 | delta = max(to - from, 0);
|
---|
| 291 | days = delta / (24L * 60L * 60L);
|
---|
| 292 | delta = delta % (24L * 60L * 60L);
|
---|
| 293 | hours = delta / (60L * 60L);
|
---|
| 294 | delta = delta % (60L * 60L);
|
---|
| 295 | minutes = delta / 60L;
|
---|
| 296 |
|
---|
| 297 | if (days > 0)
|
---|
| 298 | printf("(%ld+", days);
|
---|
| 299 | else
|
---|
| 300 | printf(" (");
|
---|
| 301 |
|
---|
| 302 | printf("%02ld:%02ld)\n", hours, minutes);
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 |
|
---|
| 306 | /* Print_Uptime() Calculate and print the "uptime" between the last recorded
|
---|
| 307 | * boot and the current time.
|
---|
| 308 | */
|
---|
| 309 | void Print_Uptime()
|
---|
| 310 | {
|
---|
| 311 | #define NLOADS 3
|
---|
| 312 | int nloads;
|
---|
| 313 | double loads[NLOADS];
|
---|
| 314 | char *utmp_file = _PATH_UTMP;
|
---|
| 315 | unsigned nusers;
|
---|
| 316 | struct utmp ut;
|
---|
| 317 | FILE *uf;
|
---|
| 318 | time_t now;
|
---|
| 319 | struct tm *tm;
|
---|
| 320 | unsigned long up;
|
---|
| 321 |
|
---|
| 322 | /* Count the number of active users in the utmp file. */
|
---|
| 323 | if ((uf = fopen(utmp_file, "r")) == NULL) {
|
---|
| 324 | fprintf(stderr, "%s: %s: %s\n", prog, utmp_file, strerror(errno));
|
---|
| 325 | exit(1);
|
---|
| 326 | }
|
---|
| 327 |
|
---|
| 328 | nusers = 0;
|
---|
| 329 | while (fread(&ut, sizeof(ut), 1, uf) == 1) {
|
---|
| 330 | #ifdef USER_PROCESS
|
---|
| 331 | if (ut.ut_type == USER_PROCESS) nusers++;
|
---|
| 332 | #else
|
---|
| 333 | if (ut.ut_name[0] != 0 && ut.ut_line[0] != 0) nusers++;
|
---|
| 334 | #endif
|
---|
| 335 | }
|
---|
| 336 | fclose(uf);
|
---|
| 337 |
|
---|
| 338 | /* Current time. */
|
---|
| 339 | now = time((time_t *) NULL);
|
---|
| 340 | tm = localtime(&now);
|
---|
| 341 |
|
---|
| 342 | /* Uptime. */
|
---|
| 343 | up = now - boot_time;
|
---|
| 344 |
|
---|
| 345 | printf(" %d:%02d up", tm->tm_hour, tm->tm_min);
|
---|
| 346 | if (up >= 24 * 3600L) {
|
---|
| 347 | unsigned long days = up / (24 * 3600L);
|
---|
| 348 | printf(" %lu day%s,", days, days == 1 ? "" : "s");
|
---|
| 349 | }
|
---|
| 350 | printf(" %lu:%02lu,", (up % (24 * 3600L)) / 3600, (up % 3600) / 60);
|
---|
| 351 | printf(" %u user%s", nusers, nusers == 1 ? "" : "s");
|
---|
| 352 | if((nloads = getloadavg(loads, NLOADS)) > 0) {
|
---|
| 353 | int i;
|
---|
| 354 | printf(", load averages:");
|
---|
| 355 | for(i = 0; i < nloads; i++)
|
---|
| 356 | printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
|
---|
| 357 | }
|
---|
| 358 | printf("\n");
|
---|
| 359 | }
|
---|
| 360 |
|
---|
| 361 |
|
---|
| 362 | /* Record_Logout_Time(wtmp) A linked list of "last logout time" is kept.
|
---|
| 363 | * Each element of the list is for one terminal.
|
---|
| 364 | */
|
---|
| 365 | void Record_Logout_Time(wtmp)
|
---|
| 366 | struct utmp *wtmp;
|
---|
| 367 | {
|
---|
| 368 | logout *link;
|
---|
| 369 |
|
---|
| 370 | /* see if the terminal is already in the list */
|
---|
| 371 | for (link = first_link; link != NULL; link = link->next)
|
---|
| 372 | if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
|
---|
| 373 | link->time = wtmp->ut_time;
|
---|
| 374 | return;
|
---|
| 375 | }
|
---|
| 376 | /* allocate a new logout record, for a tty not previously encountered */
|
---|
| 377 | link = (logout *) malloc(sizeof(logout));
|
---|
| 378 | if (link == NULL) {
|
---|
| 379 | fprintf(stderr, "%s: malloc failure\n", prog);
|
---|
| 380 | exit(1);
|
---|
| 381 | }
|
---|
| 382 | strncpy(link->line, wtmp->ut_line, (size_t)8);
|
---|
| 383 | link->time = wtmp->ut_time;
|
---|
| 384 | link->next = first_link;
|
---|
| 385 |
|
---|
| 386 | first_link = link;
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 |
|
---|
| 390 | int main(argc, argv)
|
---|
| 391 | int argc;
|
---|
| 392 | char *argv[];
|
---|
| 393 | {
|
---|
| 394 | char *wtmp_file = _PATH_WTMP;
|
---|
| 395 | FILE *f;
|
---|
| 396 | long size; /* Number of wtmp records in the file */
|
---|
| 397 | int wtmp_count; /* How many to read into wtmp_buffer */
|
---|
| 398 | struct utmp wtmp_buffer[MAX_WTMP_COUNT];
|
---|
| 399 |
|
---|
| 400 | if ((prog = strrchr(argv[0], '/')) == NULL) prog = argv[0]; else prog++;
|
---|
| 401 |
|
---|
| 402 | --argc;
|
---|
| 403 | ++argv;
|
---|
| 404 |
|
---|
| 405 | while (argc > 0 && *argv[0] == '-') {
|
---|
| 406 | if (!strcmp(argv[0], "-r"))
|
---|
| 407 | boot_limit = TRUE;
|
---|
| 408 | else
|
---|
| 409 | if (!strcmp(argv[0], "-u"))
|
---|
| 410 | tell_uptime = TRUE;
|
---|
| 411 | else if (argc > 1 && !strcmp(argv[0], "-f")) {
|
---|
| 412 | wtmp_file = argv[1];
|
---|
| 413 | --argc;
|
---|
| 414 | ++argv;
|
---|
| 415 | } else if ((print_count = atoi(argv[0] + 1)) > 0)
|
---|
| 416 | count_limit = TRUE;
|
---|
| 417 | else
|
---|
| 418 | usage();
|
---|
| 419 |
|
---|
| 420 | --argc;
|
---|
| 421 | ++argv;
|
---|
| 422 | }
|
---|
| 423 |
|
---|
| 424 | arg_count = argc;
|
---|
| 425 | args = argv;
|
---|
| 426 |
|
---|
| 427 | if (!strcmp(prog, "uptime")) tell_uptime = TRUE;
|
---|
| 428 |
|
---|
| 429 | if ((f = fopen(wtmp_file, "r")) == NULL) {
|
---|
| 430 | perror(wtmp_file);
|
---|
| 431 | exit(1);
|
---|
| 432 | }
|
---|
| 433 | if (fseek(f, 0L, 2) != 0 || (size = ftell(f)) % sizeof(struct utmp) != 0) {
|
---|
| 434 | fprintf(stderr, "%s: invalid wtmp file\n", prog);
|
---|
| 435 | exit(1);
|
---|
| 436 | }
|
---|
| 437 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
|
---|
| 438 | signal(SIGINT, Sigint);
|
---|
| 439 | signal(SIGQUIT, Sigquit);
|
---|
| 440 | }
|
---|
| 441 | size /= sizeof(struct utmp); /* Number of records in wtmp */
|
---|
| 442 |
|
---|
| 443 | if (size == 0) wtmp_buffer[0].ut_time = time((time_t *)0);
|
---|
| 444 |
|
---|
| 445 | while (size > 0) {
|
---|
| 446 | wtmp_count = (int) min(size, MAX_WTMP_COUNT);
|
---|
| 447 | size -= (long) wtmp_count;
|
---|
| 448 |
|
---|
| 449 | fseek(f, size * sizeof(struct utmp), 0);
|
---|
| 450 |
|
---|
| 451 |
|
---|
| 452 | if (fread(&wtmp_buffer[0], sizeof(struct utmp), (size_t)wtmp_count, f)
|
---|
| 453 | != wtmp_count) {
|
---|
| 454 | fprintf(stderr, "%s: read error on wtmp file\n", prog);
|
---|
| 455 | exit(1);
|
---|
| 456 | }
|
---|
| 457 | while (--wtmp_count >= 0) {
|
---|
| 458 | Process(&wtmp_buffer[wtmp_count]);
|
---|
| 459 | if (interrupt) {
|
---|
| 460 | printf("\ninterrupted %.16s \n",
|
---|
| 461 | ctime(&wtmp_buffer[wtmp_count].ut_time));
|
---|
| 462 |
|
---|
| 463 | if (interrupt == SIGINT) exit(2);
|
---|
| 464 |
|
---|
| 465 | interrupt = FALSE;
|
---|
| 466 | signal(SIGQUIT, Sigquit);
|
---|
| 467 | }
|
---|
| 468 | }
|
---|
| 469 |
|
---|
| 470 | } /* end while(size > 0) */
|
---|
| 471 |
|
---|
| 472 | if (tell_uptime) {
|
---|
| 473 | fprintf(stderr,
|
---|
| 474 | "%s: no reboot record in wtmp file to compute uptime from\n",
|
---|
| 475 | prog);
|
---|
| 476 | return(1);
|
---|
| 477 | }
|
---|
| 478 |
|
---|
| 479 | printf("\nwtmp begins %.16s \n", ctime(&wtmp_buffer[0].ut_time));
|
---|
| 480 | return(0);
|
---|
| 481 | }
|
---|