LCOV - code coverage report
Current view: top level - shared - utmp-wtmp.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 189 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 15 0.0 %

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

Generated by: LCOV version 1.11