chiark / gitweb /
Fix service file to match installed elogind binary location
[elogind.git] / src / login / eloginctl.c
1 /***
2   This file is part of elogind.
3
4   Copyright 2017 Sven Eden
5
6   elogind is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   elogind is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with elogind; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20
21 #include "bus-error.h"
22 #include "bus-util.h"
23 #include "eloginctl.h"
24 #include "logind-action.h"
25 #include "parse-util.h"
26 #include "process-util.h"
27 #include "sd-login.h"
28 #include "sd-messages.h"
29 #include "spawn-polkit-agent.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "terminal-util.h"
33 #include "user-util.h"
34 #include "virt.h"
35
36
37 elogind_action arg_action            = _ACTION_INVALID;
38 bool           arg_ask_password      = true;
39 bool           arg_ignore_inhibitors = false;
40 bool           arg_no_wall           = false;
41 BusTransport   arg_transport         = BUS_TRANSPORT_LOCAL;
42 char**         arg_wall              = NULL;
43 usec_t         arg_when              = 0;
44
45 static const struct {
46         HandleAction action;
47         const char*  verb;
48 } action_table[_ACTION_MAX] = {
49         [ACTION_POWEROFF]     = { HANDLE_POWEROFF,     "poweroff",    },
50         [ACTION_REBOOT]       = { HANDLE_REBOOT,       "reboot",      },
51         [ACTION_SUSPEND]      = { HANDLE_SUSPEND,      "suspend",     },
52         [ACTION_HIBERNATE]    = { HANDLE_HIBERNATE,    "hibernate",   },
53         [ACTION_HYBRID_SLEEP] = { HANDLE_HYBRID_SLEEP, "hybrid-sleep" },
54 };
55
56 static int elogind_set_wall_message(sd_bus* bus, const char* msg);
57
58 static enum elogind_action verb_to_action(const char *verb) {
59         enum elogind_action i;
60
61         for (i = _ACTION_INVALID; i < _ACTION_MAX; i++)
62                 if (streq_ptr(action_table[i].verb, verb))
63                         return i;
64
65         return _ACTION_INVALID;
66 }
67
68 /* Original:
69  * systemctl/systemctl.c:3292:logind_check_inhibitors()
70  */
71 static int check_inhibitors(sd_bus* bus, enum elogind_action a) {
72         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
73         _cleanup_strv_free_ char **sessions = NULL;
74         const char *what, *who, *why, *mode;
75         uint32_t uid, pid;
76         unsigned c = 0;
77         char **s;
78         int r;
79
80         if (arg_ignore_inhibitors)
81                 return 0;
82
83         if (arg_when > 0)
84                 return 0;
85
86         if (geteuid() == 0)
87                 return 0;
88
89         if (!on_tty())
90                 return 0;
91
92         if (arg_transport != BUS_TRANSPORT_LOCAL)
93                 return 0;
94
95         r = sd_bus_call_method(
96                         bus,
97                         "org.freedesktop.login1",
98                         "/org/freedesktop/login1",
99                         "org.freedesktop.login1.Manager",
100                         "ListInhibitors",
101                         NULL,
102                         &reply,
103                         NULL);
104         if (r < 0)
105                 /* If logind is not around, then there are no inhibitors... */
106                 return 0;
107
108         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
109         if (r < 0)
110                 return bus_log_parse_error(r);
111
112         while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
113                 _cleanup_free_ char *comm = NULL, *user = NULL;
114                 _cleanup_strv_free_ char **sv = NULL;
115
116                 if (!streq(mode, "block"))
117                         continue;
118
119                 sv = strv_split(what, ":");
120                 if (!sv)
121                         return log_oom();
122
123                 if ((pid_t) pid < 0)
124                         return log_error_errno(ERANGE, "Bad PID %"PRIu32": %m", pid);
125
126                 if (!strv_contains(sv,
127                                    IN_SET(a,
128                                           ACTION_HALT,
129                                           ACTION_POWEROFF,
130                                           ACTION_REBOOT,
131                                           ACTION_KEXEC) ? "shutdown" : "sleep"))
132                         continue;
133
134                 get_process_comm(pid, &comm);
135                 user = uid_to_name(uid);
136
137                 log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
138                             who, (pid_t) pid, strna(comm), strna(user), why);
139
140                 c++;
141         }
142         if (r < 0)
143                 return bus_log_parse_error(r);
144
145         r = sd_bus_message_exit_container(reply);
146         if (r < 0)
147                 return bus_log_parse_error(r);
148
149         /* Check for current sessions */
150         sd_get_sessions(&sessions);
151         STRV_FOREACH(s, sessions) {
152                 _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL;
153
154                 if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid())
155                         continue;
156
157                 if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
158                         continue;
159
160                 if (sd_session_get_type(*s, &type) < 0 || !STR_IN_SET(type, "x11", "tty"))
161                         continue;
162
163                 sd_session_get_tty(*s, &tty);
164                 sd_session_get_seat(*s, &seat);
165                 sd_session_get_service(*s, &service);
166                 user = uid_to_name(uid);
167
168                 log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty);
169                 c++;
170         }
171
172         if (c <= 0)
173                 return 0;
174
175         log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'loginctl %s -i'.",
176                   action_table[a].verb);
177
178         return -EPERM;
179 }
180
181 /* Original:
182  * systemctl/systemctl.c:8410:logind_cancel_shutdown()
183  */
184 int elogind_cancel_shutdown(sd_bus *bus) {
185         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
186         int r;
187
188         (void) elogind_set_wall_message(bus, NULL);
189
190         r = sd_bus_call_method(
191                         bus,
192                         "org.freedesktop.login1",
193                         "/org/freedesktop/login1",
194                         "org.freedesktop.login1.Manager",
195                         "CancelScheduledShutdown",
196                         &error,
197                         NULL, NULL);
198         if (r < 0)
199                 return log_warning_errno(r,
200                         "Failed to talk to elogind, shutdown hasn't been cancelled: %s",
201                         bus_error_message(&error, r));
202
203         return 0;
204 }
205
206 /* Only a little helper for cleaning up elogind specific extra stuff. */
207 void elogind_cleanup(void) {
208         polkit_agent_close();
209         strv_free(arg_wall);
210 }
211
212 /* Littel debug log helper, helps debugging systemctl comands we mimic. */
213 static void elogind_log_special(enum elogind_action a) {
214 #ifdef ENABLE_DEBUG_ELOGIND
215         switch (a) {
216         case ACTION_HALT:
217                 log_struct(LOG_INFO,
218                            LOG_MESSAGE("Halt action called."),
219                            "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
220                            NULL);
221                 break;
222         case ACTION_POWEROFF:
223                 log_struct(LOG_INFO,
224                            LOG_MESSAGE("Poweroff action called."),
225                            "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
226                            NULL);
227                 break;
228         case ACTION_REBOOT:
229                 log_struct(LOG_INFO,
230                            LOG_MESSAGE("Reboot action called."),
231                            "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
232                            NULL);
233                 break;
234         case ACTION_KEXEC:
235                 log_struct(LOG_INFO,
236                            LOG_MESSAGE("KExec action called."),
237                            "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
238                            NULL);
239                 break;
240         case ACTION_SUSPEND:
241                 log_struct(LOG_INFO,
242                            LOG_MESSAGE("Suspend action called."),
243                            "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
244                            NULL);
245                 break;
246         case ACTION_HIBERNATE:
247                 log_struct(LOG_INFO,
248                            LOG_MESSAGE("Hibernate action called."),
249                            "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
250                            NULL);
251                 break;
252         case ACTION_HYBRID_SLEEP:
253                 log_struct(LOG_INFO,
254                            LOG_MESSAGE("Hybrid-Sleep action called."),
255                            "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
256                            NULL);
257                 break;
258         case ACTION_CANCEL_SHUTDOWN:
259                 log_struct(LOG_INFO,
260                            LOG_MESSAGE("Cancel Shutdown called."),
261                            "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
262                            NULL);
263                 break;
264         default:
265                 break;
266         }
267 #endif // ENABLE_DEBUG_ELOGIND
268 }
269
270 /* Original:
271  * systemctl/systemctl.c:3229:logind_reboot()
272  */
273 static int elogind_reboot(sd_bus *bus, enum elogind_action a) {
274         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
275         const char *method  = NULL, *description = NULL;
276         int r;
277         static const char *table[_ACTION_MAX] = {
278                 [ACTION_REBOOT]          = "The system is going down for reboot NOW!",
279                 [ACTION_POWEROFF]        = "The system is going down for power-off NOW!"
280         };
281
282         if (!bus)
283                 return -EIO;
284
285         switch (a) {
286
287         case ACTION_REBOOT:
288                 method = "Reboot";
289                 description = "reboot system";
290                 break;
291
292         case ACTION_POWEROFF:
293                 method = "PowerOff";
294                 description = "power off system";
295                 break;
296
297         case ACTION_SUSPEND:
298                 method = "Suspend";
299                 description = "suspend system";
300                 break;
301
302         case ACTION_HIBERNATE:
303                 method = "Hibernate";
304                 description = "hibernate system";
305                 break;
306
307         case ACTION_HYBRID_SLEEP:
308                 method = "HybridSleep";
309                 description = "put system into hybrid sleep";
310                 break;
311
312         default:
313                 return -EINVAL;
314         }
315
316         polkit_agent_open_if_enabled();
317
318         if ( IN_SET(a, ACTION_POWEROFF, ACTION_REBOOT) )
319                 (void) elogind_set_wall_message(bus, table[a]);
320
321         /* Now call elogind itself to request the operation */
322         r = sd_bus_call_method(
323                         bus,
324                         "org.freedesktop.login1",
325                         "/org/freedesktop/login1",
326                         "org.freedesktop.login1.Manager",
327                         method,
328                         &error,
329                         NULL,
330                         "b", arg_ask_password);
331
332         if (r < 0)
333                 return log_error_errno(r, "Failed to %s via elogind: %s",
334                                        description,
335                                        bus_error_message(&error, r));
336
337         return 0;
338 }
339
340 /* Original:
341  * systemctl/systemctl.c:8281:logind_schedule_shutdown()
342  */
343 static int elogind_schedule_shutdown(sd_bus *bus, enum elogind_action a) {
344         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
345         char date[FORMAT_TIMESTAMP_MAX];
346         const char *action;
347         int r;
348
349         if (!bus)
350                 return -EIO;
351
352         switch (a) {
353         case ACTION_HALT:
354                 action = "halt";
355                 break;
356         case ACTION_POWEROFF:
357                 action = "poweroff";
358                 break;
359         case ACTION_KEXEC:
360                 action = "kexec";
361                 break;
362         case ACTION_REBOOT:
363         default:
364                 action = "reboot";
365                 break;
366         }
367
368         (void) elogind_set_wall_message(bus, NULL);
369
370         r = sd_bus_call_method(
371                         bus,
372                         "org.freedesktop.login1",
373                         "/org/freedesktop/login1",
374                         "org.freedesktop.login1.Manager",
375                         "ScheduleShutdown",
376                         &error,
377                         NULL,
378                         "st",
379                         action,
380                         arg_when);
381
382         if (r < 0)
383                 return log_warning_errno(r,
384                                 "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s",
385                                 bus_error_message(&error, r));
386
387         log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
388                  format_timestamp(date, sizeof(date), arg_when));
389
390         return 0;
391 }
392
393 /* Original:
394  * systemctl/systemctl.c:3193:logind_set_wall_message()
395  * (Tweaked to allow an extra message to be appended.)
396  */
397 static int elogind_set_wall_message(sd_bus* bus, const char* msg) {
398         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
399         _cleanup_free_ char *m = NULL;
400         int r;
401
402         if (strv_extend(&arg_wall, msg) < 0)
403                 return log_oom();
404
405         m = strv_join(arg_wall, " ");
406         if (!m)
407                 return log_oom();
408
409         r = sd_bus_call_method(
410                         bus,
411                         "org.freedesktop.login1",
412                         "/org/freedesktop/login1",
413                         "org.freedesktop.login1.Manager",
414                         "SetWallMessage",
415                         &error,
416                         NULL,
417                         "sb",
418                         m,
419                         !arg_no_wall);
420
421         if (r < 0)
422                 return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r));
423
424         return 0;
425 }
426
427 /* Original:
428  * systemctl/systemctl.c:7743:parse_shutdown_time_spec()
429  */
430 static int parse_shutdown_time_spec(const char *t, usec_t *_u) {
431         assert(t);
432         assert(_u);
433
434         if (streq(t, "now"))
435                 *_u = 0;
436         else if (!strchr(t, ':')) {
437                 uint64_t u;
438
439                 if (safe_atou64(t, &u) < 0)
440                         return -EINVAL;
441
442                 *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
443         } else {
444                 char *e = NULL;
445                 long hour, minute;
446                 struct tm tm = {};
447                 time_t s;
448                 usec_t n;
449
450                 errno = 0;
451                 hour = strtol(t, &e, 10);
452                 if (errno > 0 || *e != ':' || hour < 0 || hour > 23)
453                         return -EINVAL;
454
455                 minute = strtol(e+1, &e, 10);
456                 if (errno > 0 || *e != 0 || minute < 0 || minute > 59)
457                         return -EINVAL;
458
459                 n = now(CLOCK_REALTIME);
460                 s = (time_t) (n / USEC_PER_SEC);
461
462                 assert_se(localtime_r(&s, &tm));
463
464                 tm.tm_hour = (int) hour;
465                 tm.tm_min = (int) minute;
466                 tm.tm_sec = 0;
467
468                 assert_se(s = mktime(&tm));
469
470                 *_u = (usec_t) s * USEC_PER_SEC;
471
472                 while (*_u <= n)
473                         *_u += USEC_PER_DAY;
474         }
475
476         return 0;
477 }
478
479 /* Original:
480  * systemctl/systemctl.c:270:polkit_agent_open_if_enabled()
481  */
482 void polkit_agent_open_if_enabled(void) {
483
484         /* Open the polkit agent as a child process if necessary */
485
486         if (!arg_ask_password)
487                 return;
488
489         if (arg_transport != BUS_TRANSPORT_LOCAL)
490                 return;
491
492         polkit_agent_open();
493 }
494
495 /* Original:
496  * systemctl/systemctl.c:3482:start_special()
497  * However, this elogind variant is very different from the original.
498  */
499 int start_special(int argc, char *argv[], void *userdata) {
500         sd_bus *bus = userdata;
501         enum elogind_action a;
502         int r;
503         char** wall = NULL;
504
505         assert(argv);
506
507         a = verb_to_action(argv[0]);
508
509         elogind_log_special(a);
510
511         /* For poweroff and reboot, some extra checks are performed: */
512         if ( IN_SET(a, ACTION_POWEROFF, ACTION_REBOOT) ) {
513
514                 /* No power off actions in chroot environments */
515                 if ( running_in_chroot() > 0 ) {
516                         log_info("Running in chroot, ignoring request.");
517                         return 0;
518                 }
519
520                 /* Check time argument */
521                 if ( (argc > 1) && (ACTION_CANCEL_SHUTDOWN != arg_action)) {
522                         r = parse_shutdown_time_spec(argv[1], &arg_when);
523                         if (r < 0) {
524                                 log_error("Failed to parse time specification: %s", argv[optind]);
525                                 return r;
526                         }
527                 }
528
529                 /* The optional user wall message must be set */
530                 if ( (argc > 1)
531                   && ( (arg_action == ACTION_CANCEL_SHUTDOWN)
532                     || (0 == arg_when) ) )
533                         /* No time argument for shutdown cancel, or no
534                          * time argument given. */
535                         wall = argv + 1;
536                 else if (argc > 2)
537                         /* We skip the time argument */
538                         wall = argv + 2;
539
540                 if (wall) {
541                         arg_wall = strv_copy(wall);
542                         if (!arg_wall)
543                                 return log_oom();
544                 }
545         }
546
547         /* Switch to cancel shutdown, if a shutdown action was requested,
548            and the option to cancel it was set: */
549         if ( IN_SET(a, ACTION_POWEROFF, ACTION_REBOOT)
550           && (arg_action == ACTION_CANCEL_SHUTDOWN) )
551                 return elogind_cancel_shutdown(bus);
552
553         r = check_inhibitors(bus, a);
554         if (r < 0)
555                 return r;
556
557         /* Perform requested action */
558         if (IN_SET(a,
559                    ACTION_POWEROFF,
560                    ACTION_REBOOT,
561                    ACTION_SUSPEND,
562                    ACTION_HIBERNATE,
563                    ACTION_HYBRID_SLEEP)) {
564                 if (arg_when > 0)
565                         return elogind_schedule_shutdown(bus, a);
566                 else
567                         return elogind_reboot(bus, a);
568         }
569
570         return -EOPNOTSUPP;
571 }
572