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

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <ctype.h>
       4                 :            : #include <errno.h>
       5                 :            : #include <stdio.h>
       6                 :            : #include <sys/epoll.h>
       7                 :            : #include <sys/stat.h>
       8                 :            : #include <sys/types.h>
       9                 :            : #include <unistd.h>
      10                 :            : 
      11                 :            : #include "sd-bus.h"
      12                 :            : #include "sd-daemon.h"
      13                 :            : 
      14                 :            : #include "alloc-util.h"
      15                 :            : #include "bus-error.h"
      16                 :            : #include "bus-util.h"
      17                 :            : #include "def.h"
      18                 :            : #include "fd-util.h"
      19                 :            : #include "format-util.h"
      20                 :            : #include "initreq.h"
      21                 :            : #include "list.h"
      22                 :            : #include "log.h"
      23                 :            : #include "memory-util.h"
      24                 :            : #include "process-util.h"
      25                 :            : #include "special.h"
      26                 :            : 
      27                 :            : #define SERVER_FD_MAX 16
      28                 :            : #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
      29                 :            : 
      30                 :            : typedef struct Fifo Fifo;
      31                 :            : 
      32                 :            : typedef struct Server {
      33                 :            :         int epoll_fd;
      34                 :            : 
      35                 :            :         LIST_HEAD(Fifo, fifos);
      36                 :            :         unsigned n_fifos;
      37                 :            : 
      38                 :            :         sd_bus *bus;
      39                 :            : 
      40                 :            :         bool quit;
      41                 :            : } Server;
      42                 :            : 
      43                 :            : struct Fifo {
      44                 :            :         Server *server;
      45                 :            : 
      46                 :            :         int fd;
      47                 :            : 
      48                 :            :         struct init_request buffer;
      49                 :            :         size_t bytes_read;
      50                 :            : 
      51                 :            :         LIST_FIELDS(Fifo, fifo);
      52                 :            : };
      53                 :            : 
      54                 :          0 : static const char *translate_runlevel(int runlevel, bool *isolate) {
      55                 :            :         static const struct {
      56                 :            :                 const int runlevel;
      57                 :            :                 const char *special;
      58                 :            :                 bool isolate;
      59                 :            :         } table[] = {
      60                 :            :                 { '0', SPECIAL_POWEROFF_TARGET,   false },
      61                 :            :                 { '1', SPECIAL_RESCUE_TARGET,     true  },
      62                 :            :                 { 's', SPECIAL_RESCUE_TARGET,     true  },
      63                 :            :                 { 'S', SPECIAL_RESCUE_TARGET,     true  },
      64                 :            :                 { '2', SPECIAL_MULTI_USER_TARGET, true  },
      65                 :            :                 { '3', SPECIAL_MULTI_USER_TARGET, true  },
      66                 :            :                 { '4', SPECIAL_MULTI_USER_TARGET, true  },
      67                 :            :                 { '5', SPECIAL_GRAPHICAL_TARGET,  true  },
      68                 :            :                 { '6', SPECIAL_REBOOT_TARGET,     false },
      69                 :            :         };
      70                 :            : 
      71                 :            :         unsigned i;
      72                 :            : 
      73         [ #  # ]:          0 :         assert(isolate);
      74                 :            : 
      75         [ #  # ]:          0 :         for (i = 0; i < ELEMENTSOF(table); i++)
      76         [ #  # ]:          0 :                 if (table[i].runlevel == runlevel) {
      77                 :          0 :                         *isolate = table[i].isolate;
      78   [ #  #  #  # ]:          0 :                         if (runlevel == '6' && kexec_loaded())
      79                 :          0 :                                 return SPECIAL_KEXEC_TARGET;
      80                 :          0 :                         return table[i].special;
      81                 :            :                 }
      82                 :            : 
      83                 :          0 :         return NULL;
      84                 :            : }
      85                 :            : 
      86                 :          0 : static int change_runlevel(Server *s, int runlevel) {
      87                 :            :         const char *target;
      88                 :          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
      89                 :            :         const char *mode;
      90                 :          0 :         bool isolate = false;
      91                 :            :         int r;
      92                 :            : 
      93         [ #  # ]:          0 :         assert(s);
      94                 :            : 
      95                 :          0 :         target = translate_runlevel(runlevel, &isolate);
      96         [ #  # ]:          0 :         if (!target) {
      97         [ #  # ]:          0 :                 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
      98                 :          0 :                 return 0;
      99                 :            :         }
     100                 :            : 
     101         [ #  # ]:          0 :         if (isolate)
     102                 :          0 :                 mode = "isolate";
     103                 :            :         else
     104                 :          0 :                 mode = "replace-irreversibly";
     105                 :            : 
     106         [ #  # ]:          0 :         log_debug("Running request %s/start/%s", target, mode);
     107                 :            : 
     108                 :          0 :         r = sd_bus_call_method(
     109                 :            :                         s->bus,
     110                 :            :                         "org.freedesktop.systemd1",
     111                 :            :                         "/org/freedesktop/systemd1",
     112                 :            :                         "org.freedesktop.systemd1.Manager",
     113                 :            :                         "StartUnit",
     114                 :            :                         &error,
     115                 :            :                         NULL,
     116                 :            :                         "ss", target, mode);
     117         [ #  # ]:          0 :         if (r < 0)
     118         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to change runlevel: %s", bus_error_message(&error, -r));
     119                 :            : 
     120                 :          0 :         return 0;
     121                 :            : }
     122                 :            : 
     123                 :          0 : static void request_process(Server *s, const struct init_request *req) {
     124         [ #  # ]:          0 :         assert(s);
     125         [ #  # ]:          0 :         assert(req);
     126                 :            : 
     127         [ #  # ]:          0 :         if (req->magic != INIT_MAGIC) {
     128         [ #  # ]:          0 :                 log_error("Got initctl request with invalid magic. Ignoring.");
     129                 :          0 :                 return;
     130                 :            :         }
     131                 :            : 
     132   [ #  #  #  #  :          0 :         switch (req->cmd) {
                      # ]
     133                 :            : 
     134                 :          0 :         case INIT_CMD_RUNLVL:
     135         [ #  # ]:          0 :                 if (!isprint(req->runlevel))
     136         [ #  # ]:          0 :                         log_error("Got invalid runlevel. Ignoring.");
     137                 :            :                 else
     138      [ #  #  # ]:          0 :                         switch (req->runlevel) {
     139                 :            : 
     140                 :            :                         /* we are async anyway, so just use kill for reexec/reload */
     141                 :          0 :                         case 'u':
     142                 :            :                         case 'U':
     143         [ #  # ]:          0 :                                 if (kill(1, SIGTERM) < 0)
     144         [ #  # ]:          0 :                                         log_error_errno(errno, "kill() failed: %m");
     145                 :            : 
     146                 :            :                                 /* The bus connection will be
     147                 :            :                                  * terminated if PID 1 is reexecuted,
     148                 :            :                                  * hence let's just exit here, and
     149                 :            :                                  * rely on that we'll be restarted on
     150                 :            :                                  * the next request */
     151                 :          0 :                                 s->quit = true;
     152                 :          0 :                                 break;
     153                 :            : 
     154                 :          0 :                         case 'q':
     155                 :            :                         case 'Q':
     156         [ #  # ]:          0 :                                 if (kill(1, SIGHUP) < 0)
     157         [ #  # ]:          0 :                                         log_error_errno(errno, "kill() failed: %m");
     158                 :          0 :                                 break;
     159                 :            : 
     160                 :          0 :                         default:
     161                 :          0 :                                 (void) change_runlevel(s, req->runlevel);
     162                 :            :                         }
     163                 :          0 :                 return;
     164                 :            : 
     165                 :          0 :         case INIT_CMD_POWERFAIL:
     166                 :            :         case INIT_CMD_POWERFAILNOW:
     167                 :            :         case INIT_CMD_POWEROK:
     168         [ #  # ]:          0 :                 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
     169                 :          0 :                 return;
     170                 :            : 
     171                 :          0 :         case INIT_CMD_CHANGECONS:
     172         [ #  # ]:          0 :                 log_warning("Received console change initctl request. This is not implemented in systemd.");
     173                 :          0 :                 return;
     174                 :            : 
     175                 :          0 :         case INIT_CMD_SETENV:
     176                 :            :         case INIT_CMD_UNSETENV:
     177         [ #  # ]:          0 :                 log_warning("Received environment initctl request. This is not implemented in systemd.");
     178                 :          0 :                 return;
     179                 :            : 
     180                 :          0 :         default:
     181         [ #  # ]:          0 :                 log_warning("Received unknown initctl request. Ignoring.");
     182                 :          0 :                 return;
     183                 :            :         }
     184                 :            : }
     185                 :            : 
     186                 :          0 : static int fifo_process(Fifo *f) {
     187                 :            :         ssize_t l;
     188                 :            : 
     189         [ #  # ]:          0 :         assert(f);
     190                 :            : 
     191                 :          0 :         errno = EIO;
     192                 :          0 :         l = read(f->fd,
     193                 :          0 :                  ((uint8_t*) &f->buffer) + f->bytes_read,
     194                 :          0 :                  sizeof(f->buffer) - f->bytes_read);
     195         [ #  # ]:          0 :         if (l <= 0) {
     196         [ #  # ]:          0 :                 if (errno == EAGAIN)
     197                 :          0 :                         return 0;
     198                 :            : 
     199         [ #  # ]:          0 :                 return log_warning_errno(errno, "Failed to read from fifo: %m");
     200                 :            :         }
     201                 :            : 
     202                 :          0 :         f->bytes_read += l;
     203         [ #  # ]:          0 :         assert(f->bytes_read <= sizeof(f->buffer));
     204                 :            : 
     205         [ #  # ]:          0 :         if (f->bytes_read == sizeof(f->buffer)) {
     206                 :          0 :                 request_process(f->server, &f->buffer);
     207                 :          0 :                 f->bytes_read = 0;
     208                 :            :         }
     209                 :            : 
     210                 :          0 :         return 0;
     211                 :            : }
     212                 :            : 
     213                 :          0 : static void fifo_free(Fifo *f) {
     214         [ #  # ]:          0 :         assert(f);
     215                 :            : 
     216         [ #  # ]:          0 :         if (f->server) {
     217         [ #  # ]:          0 :                 assert(f->server->n_fifos > 0);
     218                 :          0 :                 f->server->n_fifos--;
     219   [ #  #  #  #  :          0 :                 LIST_REMOVE(fifo, f->server->fifos, f);
             #  #  #  # ]
     220                 :            :         }
     221                 :            : 
     222         [ #  # ]:          0 :         if (f->fd >= 0) {
     223         [ #  # ]:          0 :                 if (f->server)
     224                 :          0 :                         epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
     225                 :            : 
     226                 :          0 :                 safe_close(f->fd);
     227                 :            :         }
     228                 :            : 
     229                 :          0 :         free(f);
     230                 :          0 : }
     231                 :            : 
     232                 :          0 : static void server_done(Server *s) {
     233         [ #  # ]:          0 :         assert(s);
     234                 :            : 
     235         [ #  # ]:          0 :         while (s->fifos)
     236                 :          0 :                 fifo_free(s->fifos);
     237                 :            : 
     238                 :          0 :         s->epoll_fd = safe_close(s->epoll_fd);
     239                 :          0 :         s->bus = sd_bus_flush_close_unref(s->bus);
     240                 :          0 : }
     241                 :            : 
     242                 :          0 : static int server_init(Server *s, unsigned n_sockets) {
     243                 :            :         int r;
     244                 :            :         unsigned i;
     245                 :            : 
     246         [ #  # ]:          0 :         assert(s);
     247         [ #  # ]:          0 :         assert(n_sockets > 0);
     248                 :            : 
     249         [ #  # ]:          0 :         zero(*s);
     250                 :            : 
     251                 :          0 :         s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
     252         [ #  # ]:          0 :         if (s->epoll_fd < 0) {
     253         [ #  # ]:          0 :                 r = log_error_errno(errno,
     254                 :            :                                     "Failed to create epoll object: %m");
     255                 :          0 :                 goto fail;
     256                 :            :         }
     257                 :            : 
     258         [ #  # ]:          0 :         for (i = 0; i < n_sockets; i++) {
     259                 :            :                 struct epoll_event ev;
     260                 :            :                 Fifo *f;
     261                 :            :                 int fd;
     262                 :            : 
     263                 :          0 :                 fd = SD_LISTEN_FDS_START+i;
     264                 :            : 
     265                 :          0 :                 r = sd_is_fifo(fd, NULL);
     266         [ #  # ]:          0 :                 if (r < 0) {
     267         [ #  # ]:          0 :                         log_error_errno(r, "Failed to determine file descriptor type: %m");
     268                 :          0 :                         goto fail;
     269                 :            :                 }
     270                 :            : 
     271         [ #  # ]:          0 :                 if (!r) {
     272         [ #  # ]:          0 :                         log_error("Wrong file descriptor type.");
     273                 :          0 :                         r = -EINVAL;
     274                 :          0 :                         goto fail;
     275                 :            :                 }
     276                 :            : 
     277                 :          0 :                 f = new0(Fifo, 1);
     278         [ #  # ]:          0 :                 if (!f) {
     279                 :          0 :                         r = -ENOMEM;
     280         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to create fifo object: %m");
     281                 :          0 :                         goto fail;
     282                 :            :                 }
     283                 :            : 
     284                 :          0 :                 f->fd = -1;
     285                 :            : 
     286         [ #  # ]:          0 :                 zero(ev);
     287                 :          0 :                 ev.events = EPOLLIN;
     288                 :          0 :                 ev.data.ptr = f;
     289         [ #  # ]:          0 :                 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
     290                 :          0 :                         r = -errno;
     291                 :          0 :                         fifo_free(f);
     292         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
     293                 :          0 :                         goto fail;
     294                 :            :                 }
     295                 :            : 
     296                 :          0 :                 f->fd = fd;
     297   [ #  #  #  # ]:          0 :                 LIST_PREPEND(fifo, s->fifos, f);
     298                 :          0 :                 f->server = s;
     299                 :          0 :                 s->n_fifos++;
     300                 :            :         }
     301                 :            : 
     302                 :          0 :         r = bus_connect_system_systemd(&s->bus);
     303         [ #  # ]:          0 :         if (r < 0) {
     304         [ #  # ]:          0 :                 log_error_errno(r, "Failed to get D-Bus connection: %m");
     305                 :          0 :                 r = -EIO;
     306                 :          0 :                 goto fail;
     307                 :            :         }
     308                 :            : 
     309                 :          0 :         return 0;
     310                 :            : 
     311                 :          0 : fail:
     312                 :          0 :         server_done(s);
     313                 :            : 
     314                 :          0 :         return r;
     315                 :            : }
     316                 :            : 
     317                 :          0 : static int process_event(Server *s, struct epoll_event *ev) {
     318                 :            :         int r;
     319                 :            :         Fifo *f;
     320                 :            : 
     321         [ #  # ]:          0 :         assert(s);
     322                 :            : 
     323         [ #  # ]:          0 :         if (!(ev->events & EPOLLIN))
     324         [ #  # ]:          0 :                 return log_info_errno(SYNTHETIC_ERRNO(EIO),
     325                 :            :                                       "Got invalid event from epoll. (3)");
     326                 :            : 
     327                 :          0 :         f = (Fifo*) ev->data.ptr;
     328                 :          0 :         r = fifo_process(f);
     329         [ #  # ]:          0 :         if (r < 0) {
     330         [ #  # ]:          0 :                 log_info_errno(r, "Got error on fifo: %m");
     331                 :          0 :                 fifo_free(f);
     332                 :          0 :                 return r;
     333                 :            :         }
     334                 :            : 
     335                 :          0 :         return 0;
     336                 :            : }
     337                 :            : 
     338                 :          0 : int main(int argc, char *argv[]) {
     339                 :            :         Server server;
     340                 :          0 :         int r = EXIT_FAILURE, n;
     341                 :            : 
     342         [ #  # ]:          0 :         if (getppid() != 1) {
     343         [ #  # ]:          0 :                 log_error("This program should be invoked by init only.");
     344                 :          0 :                 return EXIT_FAILURE;
     345                 :            :         }
     346                 :            : 
     347         [ #  # ]:          0 :         if (argc > 1) {
     348         [ #  # ]:          0 :                 log_error("This program does not take arguments.");
     349                 :          0 :                 return EXIT_FAILURE;
     350                 :            :         }
     351                 :            : 
     352                 :          0 :         log_setup_service();
     353                 :            : 
     354                 :          0 :         umask(0022);
     355                 :            : 
     356                 :          0 :         n = sd_listen_fds(true);
     357         [ #  # ]:          0 :         if (n < 0) {
     358         [ #  # ]:          0 :                 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
     359                 :          0 :                 return EXIT_FAILURE;
     360                 :            :         }
     361                 :            : 
     362   [ #  #  #  # ]:          0 :         if (n <= 0 || n > SERVER_FD_MAX) {
     363         [ #  # ]:          0 :                 log_error("No or too many file descriptors passed.");
     364                 :          0 :                 return EXIT_FAILURE;
     365                 :            :         }
     366                 :            : 
     367         [ #  # ]:          0 :         if (server_init(&server, (unsigned) n) < 0)
     368                 :          0 :                 return EXIT_FAILURE;
     369                 :            : 
     370         [ #  # ]:          0 :         log_debug("systemd-initctl running as pid "PID_FMT, getpid_cached());
     371                 :            : 
     372                 :          0 :         sd_notify(false,
     373                 :            :                   "READY=1\n"
     374                 :            :                   "STATUS=Processing requests...");
     375                 :            : 
     376         [ #  # ]:          0 :         while (!server.quit) {
     377                 :            :                 struct epoll_event event;
     378                 :            :                 int k;
     379                 :            : 
     380                 :          0 :                 k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC);
     381         [ #  # ]:          0 :                 if (k < 0) {
     382         [ #  # ]:          0 :                         if (errno == EINTR)
     383                 :          0 :                                 continue;
     384         [ #  # ]:          0 :                         log_error_errno(errno, "epoll_wait() failed: %m");
     385                 :          0 :                         goto fail;
     386                 :            :                 }
     387                 :            : 
     388         [ #  # ]:          0 :                 if (k <= 0)
     389                 :          0 :                         break;
     390                 :            : 
     391         [ #  # ]:          0 :                 if (process_event(&server, &event) < 0)
     392                 :          0 :                         goto fail;
     393                 :            :         }
     394                 :            : 
     395                 :          0 :         r = EXIT_SUCCESS;
     396                 :            : 
     397         [ #  # ]:          0 :         log_debug("systemd-initctl stopped as pid "PID_FMT, getpid_cached());
     398                 :            : 
     399                 :          0 : fail:
     400                 :          0 :         sd_notify(false,
     401                 :            :                   "STOPPING=1\n"
     402                 :            :                   "STATUS=Shutting down...");
     403                 :            : 
     404                 :          0 :         server_done(&server);
     405                 :            : 
     406                 :          0 :         return r;
     407                 :            : }

Generated by: LCOV version 1.14