LCOV - code coverage report
Current view: top level - shared - utmp-wtmp.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 203 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 15 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 133 0.0 %

           Branch data     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