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