| /* |
| * utmp.c Routines to read/write the utmp and wtmp files. |
| * Basically just wrappers around the library routines. |
| * |
| * Version: @(#)utmp.c 2.77 09-Jun-1999 miquels@cistron.nl |
| * |
| * Copyright (C) 1999 Miquel van Smoorenburg. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <sys/utsname.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <time.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <utmpx.h> |
| |
| #include "init.h" |
| #include "initreq.h" |
| #include "paths.h" |
| |
| |
| #if defined(__GLIBC__) |
| # if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) && defined(__powerpc__) |
| # define HAVE_UPDWTMPX 0 |
| # else |
| # define HAVE_UPDWTMPX 1 |
| # endif |
| #else |
| # define HAVE_UPDWTMPX 0 |
| #endif |
| |
| #if !defined ut_time && !defined __CYGWIN__ |
| #define ut_time ut_tv.tv_sec |
| #endif |
| |
| #if defined __INTERIX || defined __FreeBSD__ |
| #ifndef RUN_LVL |
| #ifdef EMPTY |
| #define RUN_LVL EMPTY |
| #else |
| #define RUN_LVL 0 |
| #endif |
| #endif /* !RUN_LVL */ |
| #endif |
| |
| /* |
| * Log an event in the wtmpx file (reboot, runlevel) |
| */ |
| void write_wtmp( |
| const char *user, /* name of user */ |
| const char *id, /* inittab ID */ |
| int pid, /* PID of process */ |
| int type, /* TYPE of entry */ |
| const char *line) /* Which line is this */ |
| { |
| int fd; |
| struct utmpx utmp; |
| struct utsname uname_buf; |
| #if defined(__GLIBC__) || defined __SVR4 |
| struct timeval tv; |
| #endif |
| |
| /* |
| * Can't do much if WTMPX_FILE is not present or not writable. |
| */ |
| //if (access(WTMPX_FILE, W_OK) < 0) |
| // return; |
| |
| /* |
| * Try to open the wtmpx file. Note that we even try |
| * this if we have updwtmpx() so we can see if the |
| * wtmpx file is accessible. |
| */ |
| if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND)) < 0) return; |
| |
| #ifdef INIT_MAIN |
| /* |
| * Note if we are going to write a boot record. |
| */ |
| if (type == BOOT_TIME) wrote_wtmp_reboot++; |
| |
| /* |
| * See if we need to write a reboot record. The reason that |
| * we are being so paranoid is that when we first tried to |
| * write the reboot record, /var was possibly not mounted |
| * yet. As soon as we can open WTMPX we write a delayed boot record. |
| */ |
| if (wrote_wtmp_reboot == 0 && type != BOOT_TIME) |
| write_wtmp("reboot", "~~", 0, BOOT_TIME, "~"); |
| |
| /* |
| * Note if we are going to write a runlevel record. |
| */ |
| if (type == RUN_LVL) wrote_wtmp_rlevel++; |
| |
| /* |
| * See if we need to write a runlevel record. The reason that |
| * we are being so paranoid is that when we first tried to |
| * write the reboot record, /var was possibly not mounted |
| * yet. As soon as we can open WTMPX we write a delayed runlevel record. |
| */ |
| if (wrote_wtmp_rlevel == 0 && type != RUN_LVL) { |
| char line[sizeof utmp.ut_line]; |
| int runlevel = thislevel; |
| int oldlevel = prevlevel; |
| snprintf(line, sizeof line, "run-level %c", thislevel); |
| write_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, line); |
| } |
| #endif |
| |
| /* |
| * Zero the fields and enter new fields. |
| */ |
| memset(&utmp, 0, sizeof(utmp)); |
| #if defined(__GLIBC__) || defined __SVR4 |
| gettimeofday(&tv, NULL); |
| utmp.ut_tv.tv_sec = tv.tv_sec; |
| utmp.ut_tv.tv_usec = tv.tv_usec; |
| #else |
| time(&utmp.ut_time); |
| #endif |
| utmp.ut_pid = pid; |
| utmp.ut_type = type; |
| strncpy(utmp.ut_user, user, sizeof(utmp.ut_user)); |
| strncpy(utmp.ut_id , id , sizeof(utmp.ut_id )); |
| strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); |
| |
| /* Put the OS version in place of the hostname */ |
| if (uname(&uname_buf) == 0) |
| strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host)); |
| |
| #if HAVE_UPDWTMPX |
| updwtmpx(WTMPX_FILE, &utmp); |
| #else |
| write(fd, (char *)&utmp, sizeof(utmp)); |
| #endif |
| close(fd); |
| } |
| |
| /* |
| * Write an entry to the utmpx file. For DEAD_PROCESS, put |
| * the previous ut_line into oldline if oldline != NULL. |
| */ |
| static void write_utmp( |
| const char *user, /* name of user */ |
| const char *id, /* inittab ID */ |
| int pid, /* PID of process */ |
| int type, /* TYPE of entry */ |
| const char *line, /* LINE if used. */ |
| char *oldline) /* Line of old utmp entry. */ |
| { |
| struct utmpx utmp; |
| struct utmpx tmp; |
| struct utmpx *utmptr; |
| #if defined(__GLIBC__) || defined __SVR4 |
| struct timeval tv; |
| #endif |
| |
| /* |
| * Can't do much if UTMPX_FILE is not present. |
| */ |
| if (access(UTMPX_FILE, F_OK) < 0) |
| return; |
| |
| #ifdef INIT_MAIN |
| /* |
| * Note if we are going to write a boot record. |
| */ |
| if (type == BOOT_TIME) wrote_utmp_reboot++; |
| |
| /* |
| * See if we need to write a reboot record. The reason that |
| * we are being so paranoid is that when we first tried to |
| * write the reboot record, /var was possibly not mounted |
| * yet. As soon as we can open UTMPX we write a delayed boot record. |
| */ |
| if (wrote_utmp_reboot == 0 && type != BOOT_TIME) |
| write_utmp("reboot", "~~", 0, BOOT_TIME, "~", NULL); |
| |
| /* |
| * Note if we are going to write a runlevel record. |
| */ |
| if (type == RUN_LVL) wrote_utmp_rlevel++; |
| |
| /* |
| * See if we need to write a runlevel record. The reason that |
| * we are being so paranoid is that when we first tried to |
| * write the reboot record, /var was possibly not mounted |
| * yet. As soon as we can open UTMPX we write a delayed runlevel record. |
| */ |
| if (wrote_utmp_rlevel == 0 && type != RUN_LVL) { |
| char line[sizeof tmp.ut_line]; |
| int runlevel = thislevel; |
| int oldlevel = prevlevel; |
| snprintf(line, sizeof line, "run-level %c", runlevel); |
| write_utmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, line, NULL); |
| } |
| #endif |
| |
| /* |
| * Fill out an utmpx struct. |
| */ |
| memset(&utmp, 0, sizeof(utmp)); |
| utmp.ut_type = type; |
| utmp.ut_pid = pid; |
| strncpy(utmp.ut_id, id, sizeof(utmp.ut_id)); |
| #if defined(__GLIBC__) || defined __SVR4 |
| gettimeofday(&tv, NULL); |
| utmp.ut_tv.tv_sec = tv.tv_sec; |
| utmp.ut_tv.tv_usec = tv.tv_usec; |
| #else |
| time(&utmp.ut_time); |
| #endif |
| strncpy(utmp.ut_user, user, sizeof utmp.ut_user); |
| if (line) strncpy(utmp.ut_line, line, sizeof utmp.ut_line); |
| |
| /* |
| * We might need to find the existing entry first, to |
| * find the tty of the process (for wtmpx accounting). |
| */ |
| if (type == DEAD_PROCESS) { |
| /* |
| * Find existing entry for the tty line. |
| */ |
| setutxent(); |
| tmp = utmp; |
| if ((utmptr = getutxid(&tmp)) != NULL) { |
| strncpy(utmp.ut_line, utmptr->ut_line, sizeof utmptr->ut_line); |
| if (oldline) |
| strncpy(oldline, utmptr->ut_line, sizeof utmptr->ut_line); |
| } |
| } |
| |
| /* |
| * Update existing utmpx file. |
| */ |
| setutxent(); |
| pututxline(&utmp); |
| endutxent(); |
| } |
| |
| /* |
| * Write a record to both utmp and wtmp. |
| */ |
| void write_utmp_wtmp( |
| const char *user, /* name of user */ |
| const char *id, /* inittab ID */ |
| int pid, /* PID of process */ |
| int type, /* TYPE of entry */ |
| char *line) /* LINE if used. */ |
| { |
| struct utmpx tmp; |
| char oldline[sizeof tmp.ut_line]; |
| |
| /* |
| * For backwards compatibility we just return |
| * if user == NULL (means : clean up utmp file). |
| */ |
| if (user == NULL) |
| return; |
| |
| oldline[0] = 0; |
| write_utmp(user, id, pid, type, line, oldline); |
| write_wtmp(user, id, pid, type, line && line[0] ? line : oldline); |
| } |
| |