LCOV - code coverage report
Current view: top level - shared - utmp-wtmp.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 203 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 15 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <poll.h>
       6             : #include <stddef.h>
       7             : #include <stdio.h>
       8             : #include <stdlib.h>
       9             : #include <string.h>
      10             : #include <sys/time.h>
      11             : #include <sys/utsname.h>
      12             : #include <unistd.h>
      13             : #include <utmpx.h>
      14             : 
      15             : #include "alloc-util.h"
      16             : #include "fd-util.h"
      17             : #include "hostname-util.h"
      18             : #include "macro.h"
      19             : #include "memory-util.h"
      20             : #include "path-util.h"
      21             : #include "string-util.h"
      22             : #include "terminal-util.h"
      23             : #include "time-util.h"
      24             : #include "user-util.h"
      25             : #include "utmp-wtmp.h"
      26             : 
      27           0 : int utmp_get_runlevel(int *runlevel, int *previous) {
      28           0 :         struct utmpx *found, lookup = { .ut_type = RUN_LVL };
      29             :         int r;
      30             :         const char *e;
      31             : 
      32           0 :         assert(runlevel);
      33             : 
      34             :         /* If these values are set in the environment this takes
      35             :          * precedence. Presumably, sysvinit does this to work around a
      36             :          * race condition that would otherwise exist where we'd always
      37             :          * go to disk and hence might read runlevel data that might be
      38             :          * very new and does not apply to the current script being
      39             :          * executed. */
      40             : 
      41           0 :         e = getenv("RUNLEVEL");
      42           0 :         if (e && e[0] > 0) {
      43           0 :                 *runlevel = e[0];
      44             : 
      45           0 :                 if (previous) {
      46             :                         /* $PREVLEVEL seems to be an Upstart thing */
      47             : 
      48           0 :                         e = getenv("PREVLEVEL");
      49           0 :                         if (e && e[0] > 0)
      50           0 :                                 *previous = e[0];
      51             :                         else
      52           0 :                                 *previous = 0;
      53             :                 }
      54             : 
      55           0 :                 return 0;
      56             :         }
      57             : 
      58           0 :         if (utmpxname(_PATH_UTMPX) < 0)
      59           0 :                 return -errno;
      60             : 
      61           0 :         setutxent();
      62             : 
      63           0 :         found = getutxid(&lookup);
      64           0 :         if (!found)
      65           0 :                 r = -errno;
      66             :         else {
      67             :                 int a, b;
      68             : 
      69           0 :                 a = found->ut_pid & 0xFF;
      70           0 :                 b = (found->ut_pid >> 8) & 0xFF;
      71             : 
      72           0 :                 *runlevel = a;
      73           0 :                 if (previous)
      74           0 :                         *previous = b;
      75             : 
      76           0 :                 r = 0;
      77             :         }
      78             : 
      79           0 :         endutxent();
      80             : 
      81           0 :         return r;
      82             : }
      83             : 
      84           0 : static void init_timestamp(struct utmpx *store, usec_t t) {
      85           0 :         assert(store);
      86             : 
      87           0 :         if (t <= 0)
      88           0 :                 t = now(CLOCK_REALTIME);
      89             : 
      90           0 :         store->ut_tv.tv_sec = t / USEC_PER_SEC;
      91           0 :         store->ut_tv.tv_usec = t % USEC_PER_SEC;
      92           0 : }
      93             : 
      94           0 : static void init_entry(struct utmpx *store, usec_t t) {
      95           0 :         struct utsname uts = {};
      96             : 
      97           0 :         assert(store);
      98             : 
      99           0 :         init_timestamp(store, t);
     100             : 
     101           0 :         if (uname(&uts) >= 0)
     102           0 :                 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
     103             : 
     104           0 :         strncpy(store->ut_line, "~", sizeof(store->ut_line));  /* or ~~ ? */
     105           0 :         strncpy(store->ut_id, "~~", sizeof(store->ut_id));
     106           0 : }
     107             : 
     108           0 : static int write_entry_utmp(const struct utmpx *store) {
     109             :         int r;
     110             : 
     111           0 :         assert(store);
     112             : 
     113             :         /* utmp is similar to wtmp, but there is only one entry for
     114             :          * each entry type resp. user; i.e. basically a key/value
     115             :          * table. */
     116             : 
     117           0 :         if (utmpxname(_PATH_UTMPX) < 0)
     118           0 :                 return -errno;
     119             : 
     120           0 :         setutxent();
     121             : 
     122           0 :         if (!pututxline(store))
     123           0 :                 r = -errno;
     124             :         else
     125           0 :                 r = 0;
     126             : 
     127           0 :         endutxent();
     128             : 
     129           0 :         return r;
     130             : }
     131             : 
     132           0 : static int write_entry_wtmp(const struct utmpx *store) {
     133           0 :         assert(store);
     134             : 
     135             :         /* wtmp is a simple append-only file where each entry is
     136             :         simply appended to the end; i.e. basically a log. */
     137             : 
     138           0 :         errno = 0;
     139           0 :         updwtmpx(_PATH_WTMPX, store);
     140           0 :         return -errno;
     141             : }
     142             : 
     143           0 : static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
     144             :         int r, s;
     145             : 
     146           0 :         r = write_entry_utmp(store_utmp);
     147           0 :         s = write_entry_wtmp(store_wtmp);
     148             : 
     149           0 :         if (r >= 0)
     150           0 :                 r = s;
     151             : 
     152             :         /* If utmp/wtmp have been disabled, that's a good thing, hence
     153             :          * ignore the errors */
     154           0 :         if (r == -ENOENT)
     155           0 :                 r = 0;
     156             : 
     157           0 :         return r;
     158             : }
     159             : 
     160           0 : static int write_entry_both(const struct utmpx *store) {
     161           0 :         return write_utmp_wtmp(store, store);
     162             : }
     163             : 
     164           0 : int utmp_put_shutdown(void) {
     165           0 :         struct utmpx store = {};
     166             : 
     167           0 :         init_entry(&store, 0);
     168             : 
     169           0 :         store.ut_type = RUN_LVL;
     170           0 :         strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
     171             : 
     172           0 :         return write_entry_both(&store);
     173             : }
     174             : 
     175           0 : int utmp_put_reboot(usec_t t) {
     176           0 :         struct utmpx store = {};
     177             : 
     178           0 :         init_entry(&store, t);
     179             : 
     180           0 :         store.ut_type = BOOT_TIME;
     181           0 :         strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
     182             : 
     183           0 :         return write_entry_both(&store);
     184             : }
     185             : 
     186           0 : static void copy_suffix(char *buf, size_t buf_size, const char *src) {
     187             :         size_t l;
     188             : 
     189           0 :         l = strlen(src);
     190           0 :         if (l < buf_size)
     191           0 :                 strncpy(buf, src, buf_size);
     192             :         else
     193           0 :                 memcpy(buf, src + l - buf_size, buf_size);
     194           0 : }
     195             : 
     196           0 : int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
     197           0 :         struct utmpx store = {
     198             :                 .ut_type = INIT_PROCESS,
     199             :                 .ut_pid = pid,
     200             :                 .ut_session = sid,
     201             :         };
     202             :         int r;
     203             : 
     204           0 :         assert(id);
     205             : 
     206           0 :         init_timestamp(&store, 0);
     207             : 
     208             :         /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */
     209           0 :         copy_suffix(store.ut_id, sizeof(store.ut_id), id);
     210             : 
     211           0 :         if (line)
     212           0 :                 strncpy_exact(store.ut_line, line, sizeof(store.ut_line));
     213             : 
     214           0 :         r = write_entry_both(&store);
     215           0 :         if (r < 0)
     216           0 :                 return r;
     217             : 
     218           0 :         if (IN_SET(ut_type, LOGIN_PROCESS, USER_PROCESS)) {
     219           0 :                 store.ut_type = LOGIN_PROCESS;
     220           0 :                 r = write_entry_both(&store);
     221           0 :                 if (r < 0)
     222           0 :                         return r;
     223             :         }
     224             : 
     225           0 :         if (ut_type == USER_PROCESS) {
     226           0 :                 store.ut_type = USER_PROCESS;
     227           0 :                 strncpy(store.ut_user, user, sizeof(store.ut_user)-1);
     228           0 :                 r = write_entry_both(&store);
     229           0 :                 if (r < 0)
     230           0 :                         return r;
     231             :         }
     232             : 
     233           0 :         return 0;
     234             : }
     235             : 
     236           0 : int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
     237           0 :         struct utmpx lookup = {
     238             :                 .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
     239             :         }, store, store_wtmp, *found;
     240             : 
     241           0 :         assert(id);
     242             : 
     243           0 :         setutxent();
     244             : 
     245             :         /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */
     246           0 :         copy_suffix(store.ut_id, sizeof(store.ut_id), id);
     247             : 
     248           0 :         found = getutxid(&lookup);
     249           0 :         if (!found)
     250           0 :                 return 0;
     251             : 
     252           0 :         if (found->ut_pid != pid)
     253           0 :                 return 0;
     254             : 
     255           0 :         memcpy(&store, found, sizeof(store));
     256           0 :         store.ut_type = DEAD_PROCESS;
     257           0 :         store.ut_exit.e_termination = code;
     258           0 :         store.ut_exit.e_exit = status;
     259             : 
     260           0 :         zero(store.ut_user);
     261           0 :         zero(store.ut_host);
     262           0 :         zero(store.ut_tv);
     263             : 
     264           0 :         memcpy(&store_wtmp, &store, sizeof(store_wtmp));
     265             :         /* wtmp wants the current time */
     266           0 :         init_timestamp(&store_wtmp, 0);
     267             : 
     268           0 :         return write_utmp_wtmp(&store, &store_wtmp);
     269             : }
     270             : 
     271           0 : int utmp_put_runlevel(int runlevel, int previous) {
     272           0 :         struct utmpx store = {};
     273             :         int r;
     274             : 
     275           0 :         assert(runlevel > 0);
     276             : 
     277           0 :         if (previous <= 0) {
     278             :                 /* Find the old runlevel automatically */
     279             : 
     280           0 :                 r = utmp_get_runlevel(&previous, NULL);
     281           0 :                 if (r < 0) {
     282           0 :                         if (r != -ESRCH)
     283           0 :                                 return r;
     284             : 
     285           0 :                         previous = 0;
     286             :                 }
     287             :         }
     288             : 
     289           0 :         if (previous == runlevel)
     290           0 :                 return 0;
     291             : 
     292           0 :         init_entry(&store, 0);
     293             : 
     294           0 :         store.ut_type = RUN_LVL;
     295           0 :         store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
     296           0 :         strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
     297             : 
     298           0 :         return write_entry_both(&store);
     299             : }
     300             : 
     301             : #define TIMEOUT_MSEC 50
     302             : 
     303           0 : static int write_to_terminal(const char *tty, const char *message) {
     304           0 :         _cleanup_close_ int fd = -1;
     305             :         const char *p;
     306             :         size_t left;
     307             :         usec_t end;
     308             : 
     309           0 :         assert(tty);
     310           0 :         assert(message);
     311             : 
     312           0 :         fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
     313           0 :         if (fd < 0 || !isatty(fd))
     314           0 :                 return -errno;
     315             : 
     316           0 :         p = message;
     317           0 :         left = strlen(message);
     318             : 
     319           0 :         end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
     320             : 
     321           0 :         while (left > 0) {
     322             :                 ssize_t n;
     323           0 :                 struct pollfd pollfd = {
     324             :                         .fd = fd,
     325             :                         .events = POLLOUT,
     326             :                 };
     327             :                 usec_t t;
     328             :                 int k;
     329             : 
     330           0 :                 t = now(CLOCK_MONOTONIC);
     331             : 
     332           0 :                 if (t >= end)
     333           0 :                         return -ETIME;
     334             : 
     335           0 :                 k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
     336           0 :                 if (k < 0)
     337           0 :                         return -errno;
     338             : 
     339           0 :                 if (k == 0)
     340           0 :                         return -ETIME;
     341             : 
     342           0 :                 n = write(fd, p, left);
     343           0 :                 if (n < 0) {
     344           0 :                         if (errno == EAGAIN)
     345           0 :                                 continue;
     346             : 
     347           0 :                         return -errno;
     348             :                 }
     349             : 
     350           0 :                 assert((size_t) n <= left);
     351             : 
     352           0 :                 p += n;
     353           0 :                 left -= n;
     354             :         }
     355             : 
     356           0 :         return 0;
     357             : }
     358             : 
     359           0 : int utmp_wall(
     360             :         const char *message,
     361             :         const char *username,
     362             :         const char *origin_tty,
     363             :         bool (*match_tty)(const char *tty, void *userdata),
     364             :         void *userdata) {
     365             : 
     366           0 :         _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
     367             :         char date[FORMAT_TIMESTAMP_MAX];
     368             :         struct utmpx *u;
     369             :         int r;
     370             : 
     371           0 :         hn = gethostname_malloc();
     372           0 :         if (!hn)
     373           0 :                 return -ENOMEM;
     374           0 :         if (!username) {
     375           0 :                 un = getlogname_malloc();
     376           0 :                 if (!un)
     377           0 :                         return -ENOMEM;
     378             :         }
     379             : 
     380           0 :         if (!origin_tty) {
     381           0 :                 getttyname_harder(STDIN_FILENO, &stdin_tty);
     382           0 :                 origin_tty = stdin_tty;
     383             :         }
     384             : 
     385           0 :         if (asprintf(&text,
     386             :                      "\a\r\n"
     387             :                      "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
     388             :                      "%s\r\n\r\n",
     389           0 :                      un ?: username, hn,
     390             :                      origin_tty ? " on " : "", strempty(origin_tty),
     391             :                      format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
     392             :                      message) < 0)
     393           0 :                 return -ENOMEM;
     394             : 
     395           0 :         setutxent();
     396             : 
     397           0 :         r = 0;
     398             : 
     399           0 :         while ((u = getutxent())) {
     400           0 :                 _cleanup_free_ char *buf = NULL;
     401             :                 const char *path;
     402             :                 int q;
     403             : 
     404           0 :                 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
     405           0 :                         continue;
     406             : 
     407             :                 /* this access is fine, because STRLEN("/dev/") << 32 (UT_LINESIZE) */
     408           0 :                 if (path_startswith(u->ut_line, "/dev/"))
     409           0 :                         path = u->ut_line;
     410             :                 else {
     411           0 :                         if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
     412           0 :                                 return -ENOMEM;
     413             : 
     414           0 :                         path = buf;
     415             :                 }
     416             : 
     417           0 :                 if (!match_tty || match_tty(path, userdata)) {
     418           0 :                         q = write_to_terminal(path, text);
     419           0 :                         if (q < 0)
     420           0 :                                 r = q;
     421             :                 }
     422             :         }
     423             : 
     424           0 :         return r;
     425             : }

Generated by: LCOV version 1.14