1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
24 #include <sys/capability.h>
25 #include <arpa/inet.h>
28 #include "bus-label.h"
30 #include "bus-common-errors.h"
33 #include "in-addr-util.h"
34 #include "local-addresses.h"
37 static int property_get_id(
40 const char *interface
,
42 sd_bus_message
*reply
,
44 sd_bus_error
*error
) {
46 Machine
*m
= userdata
;
53 r
= sd_bus_message_append_array(reply
, 'y', &m
->id
, 16);
60 static int property_get_state(
63 const char *interface
,
65 sd_bus_message
*reply
,
67 sd_bus_error
*error
) {
69 Machine
*m
= userdata
;
77 state
= machine_state_to_string(machine_get_state(m
));
79 r
= sd_bus_message_append_basic(reply
, 's', state
);
86 static int property_get_netif(
89 const char *interface
,
91 sd_bus_message
*reply
,
93 sd_bus_error
*error
) {
95 Machine
*m
= userdata
;
102 assert_cc(sizeof(int) == sizeof(int32_t));
104 r
= sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
111 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
113 int bus_machine_method_terminate(sd_bus
*bus
, sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
114 Machine
*m
= userdata
;
125 return sd_bus_reply_method_return(message
, NULL
);
128 int bus_machine_method_kill(sd_bus
*bus
, sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
129 Machine
*m
= userdata
;
139 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
146 who
= kill_who_from_string(swho
);
148 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
151 if (signo
<= 0 || signo
>= _NSIG
)
152 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
154 r
= machine_kill(m
, who
, signo
);
158 return sd_bus_reply_method_return(message
, NULL
);
161 int bus_machine_method_get_addresses(sd_bus
*bus
, sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
162 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
163 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
164 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
165 _cleanup_close_
int netns_fd
= -1;
166 Machine
*m
= userdata
;
176 r
= readlink_malloc("/proc/self/ns/net", &us
);
178 return sd_bus_error_set_errno(error
, r
);
180 p
= procfs_file_alloca(m
->leader
, "ns/net");
181 r
= readlink_malloc(p
, &them
);
183 return sd_bus_error_set_errno(error
, r
);
186 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
188 r
= namespace_open(m
->leader
, NULL
, NULL
, &netns_fd
, NULL
);
190 return sd_bus_error_set_errno(error
, r
);
192 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
193 return sd_bus_error_set_errno(error
, -errno
);
197 return sd_bus_error_set_errno(error
, -errno
);
200 _cleanup_free_
struct local_address
*addresses
= NULL
;
201 struct local_address
*a
;
204 pair
[0] = safe_close(pair
[0]);
206 r
= namespace_enter(-1, -1, netns_fd
, -1);
210 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
214 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
215 struct iovec iov
[2] = {
216 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
217 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
220 r
= writev(pair
[1], iov
, 2);
225 pair
[1] = safe_close(pair
[1]);
230 pair
[1] = safe_close(pair
[1]);
232 r
= sd_bus_message_new_method_return(message
, &reply
);
234 return sd_bus_error_set_errno(error
, r
);
236 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
238 return sd_bus_error_set_errno(error
, r
);
243 union in_addr_union in_addr
;
250 iov
[0] = (struct iovec
) { .iov_base
= &family
, .iov_len
= sizeof(family
) };
251 iov
[1] = (struct iovec
) { .iov_base
= &in_addr
, .iov_len
= sizeof(in_addr
) };
253 n
= recvmsg(pair
[0], &mh
, 0);
255 return sd_bus_error_set_errno(error
, -errno
);
256 if ((size_t) n
< sizeof(family
))
259 r
= sd_bus_message_open_container(reply
, 'r', "iay");
261 return sd_bus_error_set_errno(error
, r
);
263 r
= sd_bus_message_append(reply
, "i", family
);
265 return sd_bus_error_set_errno(error
, r
);
270 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
271 return sd_bus_error_set_errno(error
, EIO
);
273 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
277 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
278 return sd_bus_error_set_errno(error
, EIO
);
280 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
284 return sd_bus_error_set_errno(error
, r
);
286 r
= sd_bus_message_close_container(reply
);
288 return sd_bus_error_set_errno(error
, r
);
291 r
= wait_for_terminate(child
, &si
);
293 return sd_bus_error_set_errno(error
, r
);
294 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
295 return sd_bus_error_set_errno(error
, EIO
);
297 r
= sd_bus_message_close_container(reply
);
299 return sd_bus_error_set_errno(error
, r
);
301 return sd_bus_send(bus
, reply
, NULL
);
304 int bus_machine_method_get_os_release(sd_bus
*bus
, sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
305 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
306 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
307 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
308 _cleanup_strv_free_
char **l
= NULL
;
309 _cleanup_fclose_
FILE *f
= NULL
;
310 Machine
*m
= userdata
;
320 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, &root_fd
);
324 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
332 _cleanup_close_
int fd
= -1;
334 pair
[0] = safe_close(pair
[0]);
336 r
= namespace_enter(-1, mntns_fd
, -1, root_fd
);
340 fd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
);
342 fd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
);
347 r
= copy_bytes(fd
, pair
[1], (off_t
) -1, false);
354 pair
[1] = safe_close(pair
[1]);
356 f
= fdopen(pair
[0], "re");
362 r
= load_env_file_pairs(f
, "/etc/os-release", NULL
, &l
);
366 r
= wait_for_terminate(child
, &si
);
369 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
372 r
= sd_bus_message_new_method_return(message
, &reply
);
376 r
= sd_bus_message_open_container(reply
, 'a', "{ss}");
380 STRV_FOREACH_PAIR(k
, v
, l
) {
381 r
= sd_bus_message_append(reply
, "{ss}", *k
, *v
);
386 r
= sd_bus_message_close_container(reply
);
390 return sd_bus_send(bus
, reply
, NULL
);
393 int bus_machine_method_open_pty(sd_bus
*bus
, sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
394 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, rootfd
= -1;
395 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
396 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
397 _cleanup_close_
int master
= -1;
399 struct cmsghdr cmsghdr
;
400 uint8_t buf
[CMSG_SPACE(sizeof(int))];
403 .msg_control
= &control
,
404 .msg_controllen
= sizeof(control
),
406 Machine
*m
= userdata
;
407 _cleanup_free_
char *pty_name
= NULL
;
408 struct cmsghdr
*cmsg
;
417 r
= namespace_open(m
->leader
, &pidnsfd
, &mntnsfd
, NULL
, &rootfd
);
421 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
429 pair
[0] = safe_close(pair
[0]);
431 r
= namespace_enter(pidnsfd
, mntnsfd
, -1, rootfd
);
435 master
= posix_openpt(O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
439 cmsg
= CMSG_FIRSTHDR(&mh
);
440 cmsg
->cmsg_level
= SOL_SOCKET
;
441 cmsg
->cmsg_type
= SCM_RIGHTS
;
442 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int));
443 memcpy(CMSG_DATA(cmsg
), &master
, sizeof(int));
445 mh
.msg_controllen
= cmsg
->cmsg_len
;
447 if (sendmsg(pair
[1], &mh
, MSG_NOSIGNAL
) < 0)
453 pair
[1] = safe_close(pair
[1]);
455 r
= wait_for_terminate(child
, &si
);
458 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
461 if (recvmsg(pair
[0], &mh
, MSG_NOSIGNAL
|MSG_CMSG_CLOEXEC
) < 0)
464 for (cmsg
= CMSG_FIRSTHDR(&mh
); cmsg
; cmsg
= CMSG_NXTHDR(&mh
, cmsg
))
465 if (cmsg
->cmsg_level
== SOL_SOCKET
&& cmsg
->cmsg_type
== SCM_RIGHTS
) {
469 fds
= (int*) CMSG_DATA(cmsg
);
470 n_fds
= (cmsg
->cmsg_len
- CMSG_LEN(0)) / sizeof(int);
473 close_many(fds
, n_fds
);
483 r
= ptsname_malloc(master
, &pty_name
);
487 r
= sd_bus_message_new_method_return(message
, &reply
);
491 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
495 return sd_bus_send(bus
, reply
, NULL
);
498 const sd_bus_vtable machine_vtable
[] = {
499 SD_BUS_VTABLE_START(0),
500 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
501 SD_BUS_PROPERTY("Id", "ay", property_get_id
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
502 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
503 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
504 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
505 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
506 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
507 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
508 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
509 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
510 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
511 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_CAPABILITY(CAP_KILL
)),
512 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_CAPABILITY(CAP_KILL
)),
513 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
514 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
515 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, 0),
519 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
520 Manager
*m
= userdata
;
530 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
531 _cleanup_bus_creds_unref_ sd_bus_creds
*creds
= NULL
;
532 sd_bus_message
*message
;
535 message
= sd_bus_get_current_message(bus
);
539 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
543 r
= sd_bus_creds_get_pid(creds
, &pid
);
547 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
551 _cleanup_free_
char *e
= NULL
;
554 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
558 e
= bus_label_unescape(p
);
562 machine
= hashmap_get(m
->machines
, e
);
571 char *machine_bus_path(Machine
*m
) {
572 _cleanup_free_
char *e
= NULL
;
576 e
= bus_label_escape(m
->name
);
580 return strappend("/org/freedesktop/machine1/machine/", e
);
583 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
584 _cleanup_strv_free_
char **l
= NULL
;
585 Machine
*machine
= NULL
;
586 Manager
*m
= userdata
;
594 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
597 p
= machine_bus_path(machine
);
601 r
= strv_consume(&l
, p
);
612 int machine_send_signal(Machine
*m
, bool new_machine
) {
613 _cleanup_free_
char *p
= NULL
;
617 p
= machine_bus_path(m
);
621 return sd_bus_emit_signal(
623 "/org/freedesktop/machine1",
624 "org.freedesktop.machine1.Manager",
625 new_machine
? "MachineNew" : "MachineRemoved",
629 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
630 _cleanup_bus_message_unref_ sd_bus_message
*c
= NULL
;
631 _cleanup_free_
char *p
= NULL
;
635 if (!m
->create_message
)
638 c
= m
->create_message
;
639 m
->create_message
= NULL
;
642 return sd_bus_reply_method_error(c
, error
);
644 /* Update the machine state file before we notify the client
645 * about the result. */
648 p
= machine_bus_path(m
);
652 return sd_bus_reply_method_return(c
, "o", p
);