static void test_get_process_comm(pid_t pid) { struct stat st; _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL; _cleanup_free_ char *env = NULL; char path[strlen("/proc//comm") + DECIMAL_STR_MAX(pid_t)]; pid_t e; uid_t u; gid_t g; dev_t h; int r; xsprintf(path, "/proc/"PID_FMT"/comm", pid); if (stat(path, &st) == 0) { assert_se(get_process_comm(pid, &a) >= 0); log_info("PID"PID_FMT" comm: '%s'", pid, a); } else log_warning("%s not exist.", path); assert_se(get_process_cmdline(pid, 0, true, &c) >= 0); log_info("PID"PID_FMT" cmdline: '%s'", pid, c); assert_se(get_process_cmdline(pid, 8, false, &d) >= 0); log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d); free(d); assert_se(get_process_cmdline(pid, 1, false, &d) >= 0); log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d); assert_se(get_process_ppid(pid, &e) >= 0); log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e); assert_se(pid == 1 ? e == 0 : e > 0); assert_se(is_kernel_thread(pid) == 0 || pid != 1); r = get_process_exe(pid, &f); assert_se(r >= 0 || r == -EACCES); log_info("PID"PID_FMT" exe: '%s'", pid, strna(f)); assert_se(get_process_uid(pid, &u) == 0); log_info("PID"PID_FMT" UID: "UID_FMT, pid, u); assert_se(u == 0 || pid != 1); assert_se(get_process_gid(pid, &g) == 0); log_info("PID"PID_FMT" GID: "GID_FMT, pid, g); assert_se(g == 0 || pid != 1); r = get_process_environ(pid, &env); assert_se(r >= 0 || r == -EACCES); log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno); if (!detect_container()) assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1); getenv_for_pid(pid, "PATH", &i); log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i)); }
int detect_container(void) { static const struct { const char *value; int id; } value_table[] = { { "lxc", VIRTUALIZATION_LXC }, { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT }, { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, { "docker", VIRTUALIZATION_DOCKER }, { "rkt", VIRTUALIZATION_RKT }, }; static thread_local int cached_found = _VIRTUALIZATION_INVALID; _cleanup_free_ char *m = NULL; const char *e = NULL; unsigned j; int r; if (cached_found >= 0) return cached_found; /* /proc/vz exists in container and outside of the container, * /proc/bc only outside of the container. */ if (access("/proc/vz", F_OK) >= 0 && access("/proc/bc", F_OK) < 0) { r = VIRTUALIZATION_OPENVZ; goto finish; } if (getpid() == 1) { /* If we are PID 1 we can just check our own * environment variable */ e = getenv("container"); if (isempty(e)) { r = VIRTUALIZATION_NONE; goto finish; } } else { /* Otherwise, PID 1 dropped this information into a * file in /run. This is better than accessing * /proc/1/environ, since we don't need CAP_SYS_PTRACE * for that. */ r = read_one_line_file("/run/systemd/container", &m); if (r == -ENOENT) { /* Fallback for cases where PID 1 was not * systemd (for example, cases where * init=/bin/sh is used. */ r = getenv_for_pid(1, "container", &m); if (r <= 0) { /* If that didn't work, give up, * assume no container manager. * * Note: This means we still cannot * detect containers if init=/bin/sh * is passed but privileges dropped, * as /proc/1/environ is only readable * with privileges. */ r = VIRTUALIZATION_NONE; goto finish; } } if (r < 0) return r; e = m; } for (j = 0; j < ELEMENTSOF(value_table); j++) if (streq(e, value_table[j].value)) { r = value_table[j].id; goto finish; } r = VIRTUALIZATION_CONTAINER_OTHER; finish: log_debug("Found container virtualization %s", virtualization_to_string(r)); cached_found = r; return r; }
static void test_get_process_comm(void) { struct stat st; _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL, *cwd = NULL, *root = NULL; _cleanup_free_ char *env = NULL; pid_t e; uid_t u; gid_t g; dev_t h; int r; pid_t me; if (stat("/proc/1/comm", &st) == 0) { assert_se(get_process_comm(1, &a) >= 0); log_info("pid1 comm: '%s'", a); } else log_warning("/proc/1/comm does not exist."); assert_se(get_process_cmdline(1, 0, true, &c) >= 0); log_info("pid1 cmdline: '%s'", c); assert_se(get_process_cmdline(1, 8, false, &d) >= 0); log_info("pid1 cmdline truncated: '%s'", d); assert_se(get_process_ppid(1, &e) >= 0); log_info("pid1 ppid: "PID_FMT, e); assert_se(e == 0); assert_se(is_kernel_thread(1) == 0); r = get_process_exe(1, &f); assert_se(r >= 0 || r == -EACCES); log_info("pid1 exe: '%s'", strna(f)); assert_se(get_process_uid(1, &u) == 0); log_info("pid1 uid: "UID_FMT, u); assert_se(u == 0); assert_se(get_process_gid(1, &g) == 0); log_info("pid1 gid: "GID_FMT, g); assert_se(g == 0); me = getpid(); r = get_process_cwd(me, &cwd); assert_se(r >= 0 || r == -EACCES); log_info("pid1 cwd: '%s'", cwd); r = get_process_root(me, &root); assert_se(r >= 0 || r == -EACCES); log_info("pid1 root: '%s'", root); r = get_process_environ(me, &env); assert_se(r >= 0 || r == -EACCES); log_info("self strlen(environ): '%zu'", strlen(env)); if (!detect_container()) assert_se(get_ctty_devnr(1, &h) == -ENXIO); getenv_for_pid(1, "PATH", &i); log_info("pid1 $PATH: '%s'", strna(i)); }
int main(int argc, char *argv[]) { static const char virtualization_consoles[] = "hvc0\0" "xvc0\0" "hvsi0\0" "sclp_line0\0" "ttysclp0\0" "3270!tty1\0"; _cleanup_free_ char *active = NULL; const char *j; int r; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); return EXIT_FAILURE; } if (argc > 1) arg_dest = argv[1]; log_set_target(LOG_TARGET_SAFE); log_parse_environment(); log_open(); umask(0022); if (detect_container() > 0) { _cleanup_free_ char *container_ttys = NULL; log_debug("Automatically adding console shell."); if (add_symlink("console-getty.service", "console-getty.service") < 0) return EXIT_FAILURE; /* When $container_ttys is set for PID 1, spawn * gettys on all ptys named therein. Note that despite * the variable name we only support ptys here. */ r = getenv_for_pid(1, "container_ttys", &container_ttys); if (r > 0) { const char *word, *state; size_t l; FOREACH_WORD(word, l, container_ttys, state) { const char *t; char tty[l + 1]; memcpy(tty, word, l); tty[l] = 0; /* First strip off /dev/ if it is specified */ t = path_startswith(tty, "/dev/"); if (!t) t = tty; /* Then, make sure it's actually a pty */ t = path_startswith(t, "pts/"); if (!t) continue; if (add_container_getty(t) < 0) return EXIT_FAILURE; } } /* Don't add any further magic if we are in a container */ return EXIT_SUCCESS; }
static int generate(char id[34]) { int fd, r; unsigned char *p; sd_id128_t buf; char *q; ssize_t k; const char *vm_id; assert(id); /* First, try reading the D-Bus machine id, unless it is a symlink */ fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd >= 0) { k = loop_read(fd, id, 32, false); close_nointr_nofail(fd); if (k >= 32) { id[32] = '\n'; id[33] = 0; log_info("Initializing machine ID from D-Bus machine ID."); return 0; } } /* If that didn't work, see if we are running in qemu/kvm and a * machine ID was passed in via -uuid on the qemu/kvm command * line */ r = detect_vm(&vm_id); if (r > 0 && streq(vm_id, "kvm")) { char uuid[37]; fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd >= 0) { k = loop_read(fd, uuid, 36, false); close_nointr_nofail(fd); if (k >= 36) { r = shorten_uuid(id, uuid); if (r >= 0) { log_info("Initializing machine ID from KVM UUID."); return 0; } } } } /* If that didn't work either, see if we are running in a * container, and a machine ID was passed in via * $container_uuid the way libvirt/LXC does it */ r = detect_container(NULL); if (r > 0) { char *e; r = getenv_for_pid(1, "container_uuid", &e); if (r > 0) { if (strlen(e) >= 36) { r = shorten_uuid(id, e); if (r >= 0) { log_info("Initializing machine ID from container UUID."); free(e); return 0; } } free(e); } } /* If that didn't work, generate a random machine id */ r = sd_id128_randomize(&buf); if (r < 0) { log_error("Failed to open /dev/urandom: %s", strerror(-r)); return r; } for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { q[0] = hexchar(*p >> 4); q[1] = hexchar(*p & 15); } id[32] = '\n'; id[33] = 0; log_info("Initializing machine ID from random generator."); return 0; }